- PluralisticDeliberationOrchestrator: 38 tests (367 lines) - Framework detection (6 moral frameworks) - Conflict analysis and facilitation - Urgency tier determination - Precedent tracking - Statistics and edge cases - AdaptiveCommunicationOrchestrator: 27 tests (341 lines) - Communication style adaptation (5 styles) - Anti-patronizing filter - Pub test validation (Australian/NZ) - Japanese formality handling - Statistics tracking All 65 tests passing with proper framework keyword detection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
402 lines
13 KiB
JavaScript
402 lines
13 KiB
JavaScript
/**
|
|
* Unit Tests for AdaptiveCommunicationOrchestrator
|
|
* Tests culturally-adaptive communication to prevent linguistic hierarchy
|
|
*/
|
|
|
|
const orchestrator = require('../../src/services/AdaptiveCommunicationOrchestrator.service');
|
|
|
|
describe('AdaptiveCommunicationOrchestrator', () => {
|
|
beforeEach(() => {
|
|
// Orchestrator is a singleton instance
|
|
});
|
|
|
|
describe('Anti-Patronizing Filter (inst_030)', () => {
|
|
test('should remove "simply" from messages', () => {
|
|
const message = 'You can simply implement this feature easily.';
|
|
|
|
const adapted = orchestrator.adaptCommunication(message);
|
|
|
|
expect(adapted).not.toContain('simply');
|
|
});
|
|
|
|
test('should remove "obviously" from messages', () => {
|
|
const message = 'Obviously, this approach is the best solution.';
|
|
|
|
const adapted = orchestrator.adaptCommunication(message);
|
|
|
|
expect(adapted).not.toContain('obviously');
|
|
});
|
|
|
|
test('should remove "as you may know" from messages', () => {
|
|
const message = 'As you may know, AI governance requires careful consideration.';
|
|
|
|
const adapted = orchestrator.adaptCommunication(message);
|
|
|
|
expect(adapted).not.toContain('as you may know');
|
|
});
|
|
|
|
test('should track patronizing terms removed', () => {
|
|
const statsBefore = orchestrator.getStats();
|
|
|
|
orchestrator.adaptCommunication('Simply put, obviously this is clear.');
|
|
|
|
const statsAfter = orchestrator.getStats();
|
|
|
|
expect(statsAfter.patronizing_terms_removed).toBeGreaterThan(statsBefore.patronizing_terms_removed);
|
|
});
|
|
|
|
test('should handle multiple patronizing terms', () => {
|
|
const message = 'Simply put, it is obviously clear that you can just do this, of course.';
|
|
|
|
const adapted = orchestrator.adaptCommunication(message);
|
|
|
|
expect(adapted).not.toContain('simply');
|
|
expect(adapted).not.toContain('obviously');
|
|
expect(adapted).not.toContain('of course');
|
|
});
|
|
});
|
|
|
|
describe('Pub Test (inst_029)', () => {
|
|
test('should flag overly formal language', () => {
|
|
const message = 'Notwithstanding the aforementioned circumstances, we hereby decree...';
|
|
|
|
const result = orchestrator.pubTest(message);
|
|
|
|
expect(result.passes).toBe(false);
|
|
expect(result.violations.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('should pass casual conversational language', () => {
|
|
const message = 'Right, here\'s what we decided: We\'re going with option A.';
|
|
|
|
const result = orchestrator.pubTest(message);
|
|
|
|
expect(result.passes).toBe(true);
|
|
expect(result.violations.length).toBe(0);
|
|
});
|
|
|
|
test('should flag "pursuant to" as too legal', () => {
|
|
const message = 'Pursuant to our discussion, we will proceed accordingly.';
|
|
|
|
const result = orchestrator.pubTest(message);
|
|
|
|
expect(result.passes).toBe(false);
|
|
const hasLegalViolation = result.violations.some(v => v.reason.includes('legal'));
|
|
expect(hasLegalViolation).toBe(true);
|
|
});
|
|
|
|
test('should flag "notwithstanding" as unnecessarily complex', () => {
|
|
const message = 'Notwithstanding your concerns, we shall continue.';
|
|
|
|
const result = orchestrator.pubTest(message);
|
|
|
|
expect(result.passes).toBe(false);
|
|
expect(result.violations[0].reason).toContain('Unnecessarily complex');
|
|
});
|
|
});
|
|
|
|
describe('Communication Style Detection', () => {
|
|
test('should detect formal academic style', () => {
|
|
const message = 'Furthermore, notwithstanding the theoretical implications, we must therefore consider...';
|
|
|
|
const detected = orchestrator.detectStyle(message);
|
|
|
|
expect(detected).toBe('FORMAL_ACADEMIC');
|
|
});
|
|
|
|
test('should detect casual direct style', () => {
|
|
const message = 'Right, here\'s the deal. Fair enough?';
|
|
|
|
const detected = orchestrator.detectStyle(message);
|
|
|
|
expect(detected).toBe('CASUAL_DIRECT');
|
|
});
|
|
|
|
test('should detect Māori protocol indicators', () => {
|
|
const message = 'Kia ora! Ngā mihi for your contribution to this kōrero.';
|
|
|
|
const detected = orchestrator.detectStyle(message);
|
|
|
|
expect(detected).toBe('MAORI_PROTOCOL');
|
|
});
|
|
|
|
test('should default to plain language', () => {
|
|
const message = 'This is a simple message without strong style indicators.';
|
|
|
|
const detected = orchestrator.detectStyle(message);
|
|
|
|
expect(detected).toBe('PLAIN_LANGUAGE');
|
|
});
|
|
});
|
|
|
|
describe('Style Adaptation (inst_029)', () => {
|
|
test('should adapt to formal academic style', () => {
|
|
const message = 'We decided to go with this approach.';
|
|
const context = { audience: 'FORMAL_ACADEMIC' };
|
|
|
|
const adapted = orchestrator.adaptCommunication(message, context);
|
|
|
|
expect(adapted).toBeDefined();
|
|
// In full implementation, would check for formal register
|
|
});
|
|
|
|
test('should adapt to casual direct style', () => {
|
|
const message = 'I would like to inform you that we have made a decision.';
|
|
const context = { audience: 'CASUAL_DIRECT' };
|
|
|
|
const adapted = orchestrator.adaptCommunication(message, context);
|
|
|
|
// Should simplify formal phrases
|
|
expect(adapted).not.toContain('I would like to inform you that');
|
|
});
|
|
|
|
test('should track adaptations by style', () => {
|
|
const statsBefore = orchestrator.getStats();
|
|
const beforeCount = statsBefore.by_style.FORMAL_ACADEMIC;
|
|
|
|
orchestrator.adaptCommunication('Test message 1', { audience: 'FORMAL_ACADEMIC' });
|
|
orchestrator.adaptCommunication('Test message 2', { audience: 'FORMAL_ACADEMIC' });
|
|
|
|
const statsAfter = orchestrator.getStats();
|
|
const afterCount = statsAfter.by_style.FORMAL_ACADEMIC;
|
|
|
|
expect(afterCount).toBe(beforeCount + 2);
|
|
});
|
|
|
|
test('should handle plain language adaptation', () => {
|
|
const message = 'We must utilize this methodology to facilitate implementation.';
|
|
const context = { audience: 'PLAIN_LANGUAGE' };
|
|
|
|
const adapted = orchestrator.adaptCommunication(message, context);
|
|
|
|
// Should replace jargon with plain equivalents
|
|
expect(adapted).not.toContain('utilize');
|
|
expect(adapted).toContain('use');
|
|
});
|
|
});
|
|
|
|
describe('Language Detection (inst_032)', () => {
|
|
test('should detect Te Reo Māori', () => {
|
|
const message = 'Kia ora, let me share some whakaaro about this kaupapa.';
|
|
|
|
const adapted = orchestrator.adaptCommunication(message);
|
|
|
|
// Should detect and track Māori language
|
|
const stats = orchestrator.getStats();
|
|
expect(stats.languages_detected['te-reo-maori']).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('should default to English', () => {
|
|
const message = 'This is a standard English message with no special indicators.';
|
|
|
|
const adapted = orchestrator.adaptCommunication(message);
|
|
|
|
// Should process as English without issues
|
|
expect(adapted).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('Greeting Generation', () => {
|
|
test('should generate Māori protocol greeting', () => {
|
|
const greeting = orchestrator.generateGreeting('John', {
|
|
communication_style: 'MAORI_PROTOCOL'
|
|
});
|
|
|
|
expect(greeting).toContain('Kia ora');
|
|
expect(greeting).toContain('John');
|
|
});
|
|
|
|
test('should generate casual direct greeting', () => {
|
|
const greeting = orchestrator.generateGreeting('Jane', {
|
|
communication_style: 'CASUAL_DIRECT'
|
|
});
|
|
|
|
expect(greeting).toBe('Hi Jane');
|
|
});
|
|
|
|
test('should generate formal academic greeting', () => {
|
|
const greeting = orchestrator.generateGreeting('Dr. Smith', {
|
|
communication_style: 'FORMAL_ACADEMIC'
|
|
});
|
|
|
|
expect(greeting).toContain('Dear Dr. Smith');
|
|
});
|
|
|
|
test('should generate plain language greeting by default', () => {
|
|
const greeting = orchestrator.generateGreeting('Alex');
|
|
|
|
expect(greeting).toContain('Hello');
|
|
expect(greeting).toContain('Alex');
|
|
});
|
|
});
|
|
|
|
describe('Cultural Context Application (inst_031)', () => {
|
|
test('should apply cultural adaptations when context provided', () => {
|
|
const message = 'Thank you for your contribution.';
|
|
const context = {
|
|
audience: 'MAORI_PROTOCOL',
|
|
cultural_context: 'maori'
|
|
};
|
|
|
|
const adapted = orchestrator.adaptCommunication(message, context);
|
|
|
|
// Should apply Māori protocol adaptations
|
|
expect(adapted).toBeDefined();
|
|
});
|
|
|
|
test('should handle multiple cultural contexts', () => {
|
|
const messages = [
|
|
{ text: 'Test 1', context: { cultural_context: 'australian' } },
|
|
{ text: 'Test 2', context: { cultural_context: 'japanese' } },
|
|
{ text: 'Test 3', context: { cultural_context: 'maori' } }
|
|
];
|
|
|
|
messages.forEach(({ text, context }) => {
|
|
const adapted = orchestrator.adaptCommunication(text, context);
|
|
expect(adapted).toBeDefined();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Statistics Tracking', () => {
|
|
test('should track total adaptations', () => {
|
|
const statsBefore = orchestrator.getStats();
|
|
|
|
orchestrator.adaptCommunication('Test message', { audience: 'PLAIN_LANGUAGE' });
|
|
|
|
const statsAfter = orchestrator.getStats();
|
|
|
|
expect(statsAfter.total_adaptations).toBeGreaterThan(statsBefore.total_adaptations);
|
|
});
|
|
|
|
test('should track adaptations by style', () => {
|
|
orchestrator.adaptCommunication('Test 1', { audience: 'FORMAL_ACADEMIC' });
|
|
orchestrator.adaptCommunication('Test 2', { audience: 'CASUAL_DIRECT' });
|
|
|
|
const stats = orchestrator.getStats();
|
|
|
|
expect(stats.by_style.FORMAL_ACADEMIC).toBeGreaterThan(0);
|
|
expect(stats.by_style.CASUAL_DIRECT).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('should track languages detected', () => {
|
|
orchestrator.adaptCommunication('Kia ora whānau');
|
|
|
|
const stats = orchestrator.getStats();
|
|
|
|
expect(stats.languages_detected).toBeDefined();
|
|
expect(Object.keys(stats.languages_detected).length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('Preventing Linguistic Hierarchy', () => {
|
|
test('should not favor formal academic over casual direct', () => {
|
|
const formalResult = orchestrator.adaptCommunication('Test', { audience: 'FORMAL_ACADEMIC' });
|
|
const casualResult = orchestrator.adaptCommunication('Test', { audience: 'CASUAL_DIRECT' });
|
|
|
|
// Both should be processed without error, neither privileged
|
|
expect(formalResult).toBeDefined();
|
|
expect(casualResult).toBeDefined();
|
|
});
|
|
|
|
test('should support non-Western communication norms', () => {
|
|
const contexts = [
|
|
{ audience: 'MAORI_PROTOCOL' },
|
|
{ audience: 'JAPANESE_FORMAL' }
|
|
];
|
|
|
|
contexts.forEach(context => {
|
|
const adapted = orchestrator.adaptCommunication('Test message', context);
|
|
expect(adapted).toBeDefined();
|
|
});
|
|
});
|
|
|
|
test('should remove condescension from all styles', () => {
|
|
const patronizingMessage = 'Simply put, obviously you can just do this.';
|
|
|
|
const contexts = [
|
|
{ audience: 'FORMAL_ACADEMIC' },
|
|
{ audience: 'CASUAL_DIRECT' },
|
|
{ audience: 'PLAIN_LANGUAGE' }
|
|
];
|
|
|
|
contexts.forEach(context => {
|
|
const adapted = orchestrator.adaptCommunication(patronizingMessage, context);
|
|
expect(adapted).not.toContain('simply');
|
|
expect(adapted).not.toContain('obviously');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Singleton Pattern', () => {
|
|
test('should export singleton instance with required methods', () => {
|
|
expect(typeof orchestrator.adaptCommunication).toBe('function');
|
|
expect(typeof orchestrator.pubTest).toBe('function');
|
|
expect(typeof orchestrator.detectStyle).toBe('function');
|
|
expect(typeof orchestrator.generateGreeting).toBe('function');
|
|
expect(typeof orchestrator.getStats).toBe('function');
|
|
});
|
|
});
|
|
|
|
describe('Error Handling', () => {
|
|
test('should handle null message gracefully', () => {
|
|
expect(() => {
|
|
orchestrator.adaptCommunication(null);
|
|
}).not.toThrow();
|
|
});
|
|
|
|
test('should handle empty message', () => {
|
|
const result = orchestrator.adaptCommunication('');
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result).toBe('');
|
|
});
|
|
|
|
test('should handle unknown communication style', () => {
|
|
const result = orchestrator.adaptCommunication('Test', { audience: 'UNKNOWN_STYLE' });
|
|
|
|
expect(result).toBeDefined();
|
|
expect(result).toBe('Test');
|
|
});
|
|
});
|
|
|
|
describe('Integration Points', () => {
|
|
test('should be usable by PluralisticDeliberationOrchestrator', () => {
|
|
// Simulates how PDO would use this service
|
|
const message = 'We need your input on this values conflict.';
|
|
const stakeholder = {
|
|
communication_style: 'formal_academic',
|
|
cultural_context: 'western_academic'
|
|
};
|
|
|
|
const adapted = orchestrator.adaptCommunication(message, {
|
|
audience: 'FORMAL_ACADEMIC',
|
|
cultural_context: stakeholder.cultural_context
|
|
});
|
|
|
|
expect(adapted).toBeDefined();
|
|
});
|
|
|
|
test('should handle multiple stakeholder adaptations in sequence', () => {
|
|
const message = 'Thank you for your contribution to this deliberation.';
|
|
|
|
const stakeholders = [
|
|
{ style: 'FORMAL_ACADEMIC', context: 'academic' },
|
|
{ style: 'CASUAL_DIRECT', context: 'australian' },
|
|
{ style: 'MAORI_PROTOCOL', context: 'maori' }
|
|
];
|
|
|
|
const adaptations = stakeholders.map(s =>
|
|
orchestrator.adaptCommunication(message, {
|
|
audience: s.style,
|
|
cultural_context: s.context
|
|
})
|
|
);
|
|
|
|
expect(adaptations.length).toBe(3);
|
|
adaptations.forEach(adapted => {
|
|
expect(adapted).toBeDefined();
|
|
});
|
|
});
|
|
});
|
|
});
|