Skip to main content
Glama

Analytical MCP Server

argument_structure_provider.test.ts20.8 kB
import { describe, it, expect, jest, beforeEach } from '@jest/globals'; import { ArgumentStructureProvider, SentenceAnalysisResult, IndicatorWordsResult, PremisesConclusionsResult } from '../argument_structure_provider.js'; import { ValidationHelpers } from '../../utils/validation_helpers.js'; // Mock ValidationHelpers jest.mock('../../utils/validation_helpers.js'); describe('ArgumentStructureProvider', () => { let provider: ArgumentStructureProvider; const mockValidationHelpers = ValidationHelpers as jest.Mocked<typeof ValidationHelpers>; beforeEach(() => { provider = new ArgumentStructureProvider(); jest.clearAllMocks(); // Setup default mocks mockValidationHelpers.validateNonEmptyString.mockReturnValue({ isValid: true }); mockValidationHelpers.validateDataArray.mockReturnValue({ isValid: true }); mockValidationHelpers.throwIfInvalid.mockImplementation(() => {}); }); describe('getSentenceAnalysis', () => { describe('happy path', () => { it('should analyze single sentence arguments', () => { const argument = 'This is a simple argument.'; const result = provider.getSentenceAnalysis(argument); expect(result).toEqual({ sentenceCount: 1, sentences: ['This is a simple argument'] }); expect(mockValidationHelpers.validateNonEmptyString).toHaveBeenCalledWith(argument); }); it('should analyze multi-sentence arguments with periods', () => { const argument = 'First sentence. Second sentence. Third sentence.'; const result = provider.getSentenceAnalysis(argument); expect(result).toEqual({ sentenceCount: 3, sentences: ['First sentence', 'Second sentence', 'Third sentence'] }); }); it('should handle exclamation marks and question marks', () => { const argument = 'Statement one! Is this a question? Final statement.'; const result = provider.getSentenceAnalysis(argument); expect(result).toEqual({ sentenceCount: 3, sentences: ['Statement one', 'Is this a question', 'Final statement'] }); }); it('should handle mixed punctuation', () => { const argument = 'First sentence. Second! Third? Fourth.'; const result = provider.getSentenceAnalysis(argument); expect(result.sentenceCount).toBe(4); expect(result.sentences).toEqual(['First sentence', 'Second', 'Third', 'Fourth']); }); it('should trim whitespace from sentences', () => { const argument = 'First sentence. Second sentence. Third sentence.'; const result = provider.getSentenceAnalysis(argument); expect(result.sentences).toEqual(['First sentence', 'Second sentence', 'Third sentence']); }); }); describe('edge cases', () => { it('should handle arguments with multiple consecutive punctuation marks', () => { const argument = 'First sentence... Second sentence!!! Third sentence???'; const result = provider.getSentenceAnalysis(argument); expect(result.sentenceCount).toBe(3); expect(result.sentences).toEqual(['First sentence', 'Second sentence', 'Third sentence']); }); it('should filter out empty sentences', () => { const argument = 'First sentence. . . Second sentence.'; const result = provider.getSentenceAnalysis(argument); expect(result.sentenceCount).toBe(2); expect(result.sentences).toEqual(['First sentence', 'Second sentence']); }); it('should handle arguments without terminal punctuation', () => { const argument = 'This argument has no punctuation'; const result = provider.getSentenceAnalysis(argument); expect(result.sentenceCount).toBe(1); expect(result.sentences).toEqual(['This argument has no punctuation']); }); it('should handle empty strings after splitting', () => { const argument = 'First sentence.. . Second sentence.'; const result = provider.getSentenceAnalysis(argument); expect(result.sentences).not.toContain(''); expect(result.sentences.length).toBe(2); }); }); describe('error handling', () => { it('should throw error for empty string', () => { mockValidationHelpers.validateNonEmptyString.mockReturnValue({ isValid: false, error: 'Empty string' }); mockValidationHelpers.throwIfInvalid.mockImplementation((result) => { if (!result.isValid) throw new Error(result.error); }); expect(() => provider.getSentenceAnalysis('')).toThrow('Empty string'); }); it('should throw error for null input', () => { mockValidationHelpers.validateNonEmptyString.mockReturnValue({ isValid: false, error: 'Null input' }); mockValidationHelpers.throwIfInvalid.mockImplementation((result) => { if (!result.isValid) throw new Error(result.error); }); expect(() => provider.getSentenceAnalysis(null as any)).toThrow('Null input'); }); }); }); describe('getIndicatorWords', () => { describe('happy path', () => { it('should return predefined indicator words', () => { const result = provider.getIndicatorWords(); expect(result).toHaveProperty('conclusionIndicators'); expect(result).toHaveProperty('premiseIndicators'); expect(Array.isArray(result.conclusionIndicators)).toBe(true); expect(Array.isArray(result.premiseIndicators)).toBe(true); }); it('should include common conclusion indicators', () => { const result = provider.getIndicatorWords(); expect(result.conclusionIndicators).toContain('therefore'); expect(result.conclusionIndicators).toContain('thus'); expect(result.conclusionIndicators).toContain('hence'); expect(result.conclusionIndicators).toContain('consequently'); expect(result.conclusionIndicators).toContain('so'); }); it('should include common premise indicators', () => { const result = provider.getIndicatorWords(); expect(result.premiseIndicators).toContain('because'); expect(result.premiseIndicators).toContain('since'); expect(result.premiseIndicators).toContain('as'); expect(result.premiseIndicators).toContain('given that'); expect(result.premiseIndicators).toContain('for'); }); it('should return consistent results across calls', () => { const result1 = provider.getIndicatorWords(); const result2 = provider.getIndicatorWords(); expect(result1).toEqual(result2); }); }); }); describe('identifyPremisesAndConclusions', () => { const sampleSentences = ['First sentence', 'Second sentence', 'Third sentence']; const sampleConclusionIndicators = ['therefore', 'thus', 'hence']; const samplePremiseIndicators = ['because', 'since', 'as']; describe('happy path', () => { it('should identify conclusions with indicator words', () => { const sentences = ['All men are mortal', 'Therefore, Socrates is mortal']; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); expect(result.potentialConclusions).toContain('Therefore, Socrates is mortal'); expect(result.potentialPremises).toContain('All men are mortal'); }); it('should identify premises with indicator words', () => { const sentences = ['Since it is raining, the ground will be wet']; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); expect(result.potentialPremises).toContain('Since it is raining, the ground will be wet'); }); it('should handle case-insensitive indicator matching', () => { const sentences = ['BECAUSE it is true', 'THEREFORE we conclude']; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); expect(result.potentialPremises).toContain('BECAUSE it is true'); expect(result.potentialConclusions).toContain('THEREFORE we conclude'); }); it('should apply position heuristic when no indicators present', () => { const sentences = ['First statement', 'Second statement', 'Final statement']; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); expect(result.potentialConclusions).toContain('Final statement'); expect(result.potentialPremises).toContain('First statement'); expect(result.potentialPremises).toContain('Second statement'); }); it('should prioritize indicator words over position heuristic', () => { const sentences = ['Therefore, we conclude this first', 'Because of evidence', 'Final statement']; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); expect(result.potentialConclusions).toContain('Therefore, we conclude this first'); expect(result.potentialPremises).toContain('Because of evidence'); expect(result.potentialConclusions).toContain('Final statement'); // Position heuristic for remaining }); it('should handle multiple premises and conclusions', () => { const sentences = [ 'Because A is true', 'Since B is also true', 'Therefore C follows', 'Hence D must be true' ]; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); expect(result.potentialPremises).toHaveLength(2); expect(result.potentialConclusions).toHaveLength(2); }); }); describe('edge cases', () => { it('should handle single sentence arguments', () => { const sentences = ['Single sentence argument']; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); // Single sentence should be treated as conclusion by position heuristic expect(result.potentialConclusions).toContain('Single sentence argument'); expect(result.potentialPremises).toHaveLength(0); }); it('should handle empty sentences array', () => { const result = provider.identifyPremisesAndConclusions( [], sampleConclusionIndicators, samplePremiseIndicators ); expect(result.potentialPremises).toHaveLength(0); expect(result.potentialConclusions).toHaveLength(0); }); it('should handle sentences with partial indicator word matches', () => { const sentences = ['Therein lies the problem', 'Foremost among reasons']; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); // Should not match partial words, apply position heuristic instead expect(result.potentialPremises).toContain('Therein lies the problem'); expect(result.potentialConclusions).toContain('Foremost among reasons'); }); it('should prefer conclusion indicators over premise indicators', () => { const sentences = ['Because therefore we conclude this']; const result = provider.identifyPremisesAndConclusions( sentences, sampleConclusionIndicators, samplePremiseIndicators ); // Should identify as conclusion since it's checked first expect(result.potentialConclusions).toContain('Because therefore we conclude this'); expect(result.potentialPremises).toHaveLength(0); }); }); describe('error handling', () => { it('should throw error for invalid sentences array', () => { mockValidationHelpers.validateDataArray.mockReturnValueOnce({ isValid: false, error: 'Invalid sentences' }); mockValidationHelpers.throwIfInvalid.mockImplementation((result) => { if (!result.isValid) throw new Error(result.error); }); expect(() => provider.identifyPremisesAndConclusions( sampleSentences, sampleConclusionIndicators, samplePremiseIndicators )).toThrow('Invalid sentences'); }); it('should throw error for invalid conclusion indicators', () => { mockValidationHelpers.validateDataArray .mockReturnValueOnce({ isValid: true }) .mockReturnValueOnce({ isValid: false, error: 'Invalid conclusion indicators' }); mockValidationHelpers.throwIfInvalid.mockImplementation((result) => { if (!result.isValid) throw new Error(result.error); }); expect(() => provider.identifyPremisesAndConclusions( sampleSentences, sampleConclusionIndicators, samplePremiseIndicators )).toThrow('Invalid conclusion indicators'); }); it('should throw error for invalid premise indicators', () => { mockValidationHelpers.validateDataArray .mockReturnValueOnce({ isValid: true }) .mockReturnValueOnce({ isValid: true }) .mockReturnValueOnce({ isValid: false, error: 'Invalid premise indicators' }); mockValidationHelpers.throwIfInvalid.mockImplementation((result) => { if (!result.isValid) throw new Error(result.error); }); expect(() => provider.identifyPremisesAndConclusions( sampleSentences, sampleConclusionIndicators, samplePremiseIndicators )).toThrow('Invalid premise indicators'); }); }); }); describe('analyzeArgumentStructure', () => { describe('happy path', () => { it('should analyze simple single sentence argument', () => { const argument = 'This is a simple argument.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('### Argument Structure Analysis'); expect(result).toContain('**Argument Complexity:** 1 sentence(s)'); expect(result).toContain('**simple argument** contained within a single sentence'); expect(result).toContain('**Identified Premises:**'); expect(result).toContain('**Identified Conclusions:**'); }); it('should analyze basic two-sentence argument', () => { const argument = 'All men are mortal. Socrates is mortal.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('**Argument Complexity:** 2 sentence(s)'); expect(result).toContain('**basic argument** with premise(s) and conclusion'); }); it('should analyze complex multi-sentence argument', () => { const argument = 'First premise. Second premise. Third premise. Conclusion follows.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('**Argument Complexity:** 4 sentence(s)'); expect(result).toContain('**complex argument** with multiple premises'); }); it('should identify conditional (if-then) patterns', () => { const argument = 'If it rains, then the ground gets wet. Therefore, it will be muddy.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('**conditional (if-then) pattern**'); }); it('should identify conditional patterns with just if-therefore', () => { const argument = 'If the economy improves, unemployment will decrease. Therefore, people will be happier.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('**conditional (if-then) pattern**'); }); it('should identify arguments with multiple conclusions', () => { const argument = 'Given the evidence. Therefore, A is true. Hence, B follows.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('**multiple conclusions** or sub-conclusions'); }); it('should display identified premises and conclusions', () => { const argument = 'Because it is raining, the ground is wet. Therefore, we should stay inside.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('1. Because it is raining, the ground is wet'); expect(result).toContain('1. Therefore, we should stay inside'); }); it('should handle arguments with no clear premises or conclusions', () => { const argument = 'Some statement. Another statement.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('1. Some statement'); expect(result).toContain('1. Another statement'); }); }); describe('edge cases', () => { it('should handle arguments with only punctuation variations', () => { const argument = 'Statement one! Statement two? Statement three.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('**Argument Complexity:** 3 sentence(s)'); expect(result).toContain('**complex argument**'); }); it('should handle case-insensitive conditional pattern matching', () => { const argument = 'IF conditions are met, THEN results follow. THEREFORE conclusion.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('**conditional (if-then) pattern**'); }); it('should handle mixed case indicator words', () => { const argument = 'BECAUSE evidence exists. THEREFORE conclusion follows.'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('1. BECAUSE evidence exists'); expect(result).toContain('1. THEREFORE conclusion follows'); }); it('should handle arguments with no terminal punctuation', () => { const argument = 'This is an argument without punctuation'; const result = provider.analyzeArgumentStructure(argument); expect(result).toContain('**Argument Complexity:** 1 sentence(s)'); expect(result).toContain('**simple argument**'); }); }); describe('error handling', () => { it('should throw error for empty string', () => { mockValidationHelpers.validateNonEmptyString.mockReturnValue({ isValid: false, error: 'Empty string' }); mockValidationHelpers.throwIfInvalid.mockImplementation((result) => { if (!result.isValid) throw new Error(result.error); }); expect(() => provider.analyzeArgumentStructure('')).toThrow('Empty string'); }); it('should throw error for null input', () => { mockValidationHelpers.validateNonEmptyString.mockReturnValue({ isValid: false, error: 'Null input' }); mockValidationHelpers.throwIfInvalid.mockImplementation((result) => { if (!result.isValid) throw new Error(result.error); }); expect(() => provider.analyzeArgumentStructure(null as any)).toThrow('Null input'); }); }); }); describe('performance', () => { it('should handle long arguments efficiently', () => { const longArgument = 'This is a sentence. '.repeat(100) + 'Therefore, this is the conclusion.'; const startTime = Date.now(); const result = provider.analyzeArgumentStructure(longArgument); const endTime = Date.now(); expect(endTime - startTime).toBeLessThan(1000); // Should complete within 1 second expect(result).toContain('### Argument Structure Analysis'); expect(result).toContain('**Argument Complexity:** 101 sentence(s)'); }); it('should handle multiple rapid analyses', () => { const testArguments = [ 'Simple argument.', 'Premise. Conclusion.', 'If this, then that. Therefore, result.', 'Because reason. Hence, conclusion.' ]; const startTime = Date.now(); const results = testArguments.map(arg => provider.analyzeArgumentStructure(arg)); const endTime = Date.now(); expect(results).toHaveLength(4); expect(endTime - startTime).toBeLessThan(500); results.forEach(result => { expect(result).toContain('### Argument Structure Analysis'); }); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/quanticsoul4772/analytical-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server