package workflow import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "go.temporal.io/sdk/testsuite" ) // Stub activity functions for test registration func ChunkAndEmbedActivity(_ context.Context, _ SynthesisInput) (string, error) { return "", nil } func ExtractEntitiesActivity(_ context.Context, _ string, _ string) ([]ExtractedEntity, error) { return nil, nil } func ExtractFactsActivity(_ context.Context, _ string, _ string, _ []ExtractedEntity) ([]ExtractedFact, error) { return nil, nil } func DetectContradictionsActivity(_ context.Context, _ string, _ []ExtractedFact) ([]ContradictionResult, error) { return nil, nil } func PersistSynthesisActivity(_ context.Context, _ PersistInput) error { return nil } func registerSynthesisActivities(env *testsuite.TestWorkflowEnvironment) { env.RegisterActivity(ChunkAndEmbedActivity) env.RegisterActivity(ExtractEntitiesActivity) env.RegisterActivity(ExtractFactsActivity) env.RegisterActivity(DetectContradictionsActivity) env.RegisterActivity(PersistSynthesisActivity) } func TestSynthesisWorkflow_Success(t *testing.T) { ts := &testsuite.WorkflowTestSuite{} env := ts.NewTestWorkflowEnvironment() input := SynthesisInput{ Project: "poimen", Source: "transcript://test-123", Text: "Kubernetes uses port 8080 for the API server", Kind: "L1", } registerSynthesisActivities(env) // Stage 1: Chunk + Embed env.OnActivity(ChunkAndEmbedActivity, mock.Anything, input).Return("chunk-abc123", nil) // Stage 2: Entity Extraction entities := []ExtractedEntity{ {Name: "Kubernetes", EntityType: "tool", Confidence: 0.95}, {Name: "API server", EntityType: "component", Confidence: 0.90}, } env.OnActivity(ExtractEntitiesActivity, mock.Anything, "chunk-abc123", input.Text).Return(entities, nil) // Stage 3: Fact Extraction facts := []ExtractedFact{ {Subject: "Kubernetes", Predicate: "uses_port", Object: "8080", Confidence: 0.85}, } env.OnActivity(ExtractFactsActivity, mock.Anything, "chunk-abc123", input.Text, entities).Return(facts, nil) // Stage 4: Contradiction Detection contradictions := []ContradictionResult{} env.OnActivity(DetectContradictionsActivity, mock.Anything, "poimen", facts).Return(contradictions, nil) // Stage 5: Persist env.OnActivity(PersistSynthesisActivity, mock.Anything, mock.Anything).Return(nil) env.ExecuteWorkflow(SynthesisWorkflow, input) assert.True(t, env.IsWorkflowCompleted()) assert.NoError(t, env.GetWorkflowError()) var result SynthesisResult assert.NoError(t, env.GetWorkflowResult(&result)) assert.Equal(t, "chunk-abc123", result.ChunkID) assert.Equal(t, 2, result.EntitiesExtracted) assert.Equal(t, 1, result.FactsExtracted) assert.Equal(t, 0, result.Contradictions) assert.Equal(t, 0, result.ReviewQueued) } func TestSynthesisWorkflow_WithContradictions(t *testing.T) { ts := &testsuite.WorkflowTestSuite{} env := ts.NewTestWorkflowEnvironment() registerSynthesisActivities(env) input := SynthesisInput{ Project: "poimen", Source: "transcript://test-456", Text: "Port 8080 is used by nginx", Kind: "L1", } env.OnActivity(ChunkAndEmbedActivity, mock.Anything, input).Return("chunk-def456", nil) entities := []ExtractedEntity{ {Name: "nginx", EntityType: "tool", Confidence: 0.92}, } env.OnActivity(ExtractEntitiesActivity, mock.Anything, "chunk-def456", input.Text).Return(entities, nil) facts := []ExtractedFact{ {Subject: "nginx", Predicate: "uses_port", Object: "8080", Confidence: 0.88}, } env.OnActivity(ExtractFactsActivity, mock.Anything, "chunk-def456", input.Text, entities).Return(facts, nil) contradictions := []ContradictionResult{ { FactA: ExtractedFact{Subject: "Kubernetes", Predicate: "uses_port", Object: "8080"}, FactB: ExtractedFact{Subject: "nginx", Predicate: "uses_port", Object: "8080"}, Severity: "medium", AutoResolved: false, QueuedReview: true, }, } env.OnActivity(DetectContradictionsActivity, mock.Anything, "poimen", facts).Return(contradictions, nil) env.OnActivity(PersistSynthesisActivity, mock.Anything, mock.Anything).Return(nil) env.ExecuteWorkflow(SynthesisWorkflow, input) assert.True(t, env.IsWorkflowCompleted()) assert.NoError(t, env.GetWorkflowError()) var result SynthesisResult assert.NoError(t, env.GetWorkflowResult(&result)) assert.Equal(t, 1, result.Contradictions) assert.Equal(t, 1, result.ReviewQueued) } func TestSynthesisWorkflow_EntityExtractionFails(t *testing.T) { ts := &testsuite.WorkflowTestSuite{} env := ts.NewTestWorkflowEnvironment() registerSynthesisActivities(env) input := SynthesisInput{ Project: "poimen", Source: "transcript://test-789", Text: "Some text", Kind: "L1", } env.OnActivity(ChunkAndEmbedActivity, mock.Anything, input).Return("chunk-xyz", nil) env.OnActivity(ExtractEntitiesActivity, mock.Anything, "chunk-xyz", input.Text). Return(nil, assert.AnError) env.ExecuteWorkflow(SynthesisWorkflow, input) assert.True(t, env.IsWorkflowCompleted()) assert.Error(t, env.GetWorkflowError()) assert.Contains(t, env.GetWorkflowError().Error(), "stage 2 entity extraction") } func TestSynthesisWorkflow_ChunkFails(t *testing.T) { ts := &testsuite.WorkflowTestSuite{} env := ts.NewTestWorkflowEnvironment() registerSynthesisActivities(env) input := SynthesisInput{ Project: "poimen", Source: "transcript://test-fail", Text: "Bad text", Kind: "L1", } env.OnActivity(ChunkAndEmbedActivity, mock.Anything, input).Return("", assert.AnError) env.ExecuteWorkflow(SynthesisWorkflow, input) assert.True(t, env.IsWorkflowCompleted()) assert.Error(t, env.GetWorkflowError()) assert.Contains(t, env.GetWorkflowError().Error(), "stage 1 chunk+embed") } func TestSynthesisInput_Fields(t *testing.T) { input := SynthesisInput{ Project: "test", Source: "source://1", Text: "hello", Kind: "L2", Tags: []string{"tag1", "tag2"}, } assert.Equal(t, "test", input.Project) assert.Equal(t, "L2", input.Kind) assert.Len(t, input.Tags, 2) }