-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinsight-provider.test.ts
More file actions
161 lines (145 loc) · 5.92 KB
/
Copy pathinsight-provider.test.ts
File metadata and controls
161 lines (145 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import fs from "fs";
import os from "os";
import path from "path";
import { afterEach, describe, expect, it } from "vitest";
import { ExternalInsightSchema } from "agent-contracts-analyzer";
import {
MicroContractsInsightProvider,
buildExternalInsight,
filterInsight,
} from "../../src/external/insight-provider.js";
const EXAMPLES_ROOT = path.resolve(import.meta.dirname, "../../examples");
describe("MicroContractsInsightProvider", () => {
it("implements InsightProvider and validates against ExternalInsightSchema", async () => {
const provider = new MicroContractsInsightProvider();
expect(provider.name).toBe("micro-contracts");
const insight = await provider.provide({ projectRoot: EXAMPLES_ROOT });
const parsed = ExternalInsightSchema.safeParse(insight);
expect(parsed.success).toBe(true);
expect(insight.source).toBe("micro-contracts");
expect(insight.sourceVersion).toBeDefined();
expect(insight.edges.length).toBeGreaterThan(0);
expect(insight.anchorMapping?.length).toBeGreaterThan(0);
});
it("emits module-level api_dependency edges with api_contract_declaration evidence", () => {
const insight = buildExternalInsight(EXAMPLES_ROOT);
const moduleEdge = insight.edges.find(
(e) =>
e.from === "billing" &&
e.to === "core.User.getUsers" &&
e.kind === "api_dependency",
);
expect(moduleEdge).toBeDefined();
expect(moduleEdge!.propagation).toBe("forward");
expect(moduleEdge!.weight).toBe(0.9);
expect(moduleEdge!.evidence?.[0]?.kind).toBe("api_contract_declaration");
});
it("emits operation-level api_operation_dependency edges", () => {
const insight = buildExternalInsight(EXAMPLES_ROOT);
const opEdge = insight.edges.find(
(e) =>
e.from === "billing.Billing.createInvoice" &&
e.to === "core.User.getUserById" &&
e.kind === "api_operation_dependency",
);
expect(opEdge).toBeDefined();
expect(opEdge!.evidence?.[0]?.symbolId).toBe(
"packages/contract/billing/services/BillingServiceApi.ts#createInvoice",
);
});
it("maps modules to OpenAPI and contract package paths", () => {
const insight = buildExternalInsight(EXAMPLES_ROOT);
const billingAnchor = insight.anchorMapping?.find((a) => a.domainId === "billing");
expect(billingAnchor).toBeDefined();
expect(billingAnchor!.filePaths).toContain("spec/billing/openapi/billing.yaml");
expect(billingAnchor!.filePaths).toContain("packages/contract/billing");
});
it("includes symbol-level AnchorMapping for API refs", () => {
const insight = buildExternalInsight(EXAMPLES_ROOT);
const apiAnchor = insight.anchorMapping?.find(
(a) => a.domainId === "core.User.getUserById",
);
expect(apiAnchor).toBeDefined();
expect(apiAnchor!.symbolIds).toContain(
"packages/contract/core/services/UserServiceApi.ts#getUserById",
);
expect(apiAnchor!.symbols).toHaveLength(1);
expect(apiAnchor!.symbols![0]!.symbolId).toBe(
"packages/contract/core/services/UserServiceApi.ts#getUserById",
);
});
it("includes symbol-level AnchorMapping for source operations", () => {
const insight = buildExternalInsight(EXAMPLES_ROOT);
const opAnchor = insight.anchorMapping?.find(
(a) => a.domainId === "billing.Billing.createInvoice",
);
expect(opAnchor).toBeDefined();
expect(opAnchor!.symbols?.[0]?.symbolId).toBe(
"packages/contract/billing/services/BillingServiceApi.ts#createInvoice",
);
});
it("filters edges by changedFiles to those touching the matching module", async () => {
const provider = new MicroContractsInsightProvider();
const filtered = await provider.provide({
projectRoot: EXAMPLES_ROOT,
changedFiles: ["spec/billing/openapi/billing.yaml"],
});
expect(filtered.edges.length).toBeGreaterThan(0);
expect(
filtered.edges.some(
(e) =>
e.from === "billing" &&
e.to === "core.User.getUsers" &&
e.kind === "api_dependency",
),
).toBe(true);
for (const edge of filtered.edges) {
const touchesBillingModule =
edge.from === "billing" || edge.from.startsWith("billing.");
const touchesBillingDependency =
edge.to === "billing" || edge.to.startsWith("billing.");
expect(touchesBillingModule || touchesBillingDependency).toBe(true);
}
expect(filtered.anchorMapping?.some((a) => a.domainId === "billing")).toBe(
true,
);
});
it("filters edges by artifactIds", async () => {
const provider = new MicroContractsInsightProvider();
const fullInsight = buildExternalInsight(EXAMPLES_ROOT);
const filtered = await provider.provide({
projectRoot: EXAMPLES_ROOT,
artifactIds: ["billing.Billing.createInvoice"],
});
expect(filtered.edges.length).toBeLessThan(fullInsight.edges.length);
expect(filtered.edges).toHaveLength(1);
expect(filtered.edges[0]).toMatchObject({
from: "billing.Billing.createInvoice",
to: "core.User.getUserById",
kind: "api_operation_dependency",
});
expect(filtered.anchorMapping?.map((a) => a.domainId).sort()).toEqual(
["billing.Billing.createInvoice", "core.User.getUserById"].sort(),
);
});
it("returns all edges when no filters are specified", () => {
const insight = buildExternalInsight(EXAMPLES_ROOT);
const unfiltered = filterInsight(insight, { projectRoot: EXAMPLES_ROOT });
expect(unfiltered.edges).toEqual(insight.edges);
expect(unfiltered.anchorMapping).toEqual(insight.anchorMapping);
});
});
describe("buildExternalInsight error handling", () => {
const tempDirs: string[] = [];
afterEach(() => {
for (const dir of tempDirs) {
fs.rmSync(dir, { recursive: true, force: true });
}
tempDirs.length = 0;
});
it("throws when config is missing", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "mc-insight-"));
tempDirs.push(dir);
expect(() => buildExternalInsight(dir)).toThrow(/config/i);
});
});