Skip to content

Commit 8448ed6

Browse files
committed
feat: improve tests, fix cache and fix package
1 parent dc4ec10 commit 8448ed6

4 files changed

Lines changed: 193 additions & 2 deletions

File tree

packages/enricher/package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@
77
".": {
88
"types": "./dist/index.d.ts",
99
"import": "./dist/index.js"
10+
},
11+
"./classification": {
12+
"types": "./dist/flag-classification.d.ts",
13+
"import": "./dist/flag-classification.js"
14+
},
15+
"./stale-flags": {
16+
"types": "./dist/stale-flags.d.ts",
17+
"import": "./dist/stale-flags.js"
18+
},
19+
"./types": {
20+
"types": "./dist/types.d.ts",
21+
"import": "./dist/types.js"
1022
}
1123
},
1224
"scripts": {
@@ -28,7 +40,6 @@
2840
},
2941
"files": [
3042
"dist/**/*",
31-
"src/**/*",
3243
"grammars/**/*"
3344
]
3445
}

packages/enricher/src/detector.test.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,4 +447,168 @@ describeWithGrammars("PostHogDetector", () => {
447447
]);
448448
});
449449
});
450+
451+
// ═══════════════════════════════════════════════════
452+
// Python — additional findPostHogCalls / findInitCalls
453+
// ═══════════════════════════════════════════════════
454+
455+
describe("Python — findPostHogCalls (capture)", () => {
456+
test("detects capture with positional event arg", async () => {
457+
const code = `posthog.capture('user_id', 'purchase')`;
458+
const calls = await detector.findPostHogCalls(code, "python");
459+
const capture = calls.find(
460+
(c) => c.method === "capture" && c.key === "purchase",
461+
);
462+
expect(capture).toBeDefined();
463+
});
464+
465+
test("detects flag method get_feature_flag", async () => {
466+
const code = `posthog.get_feature_flag('my-flag')`;
467+
const calls = await detector.findPostHogCalls(code, "python");
468+
expect(simpleCalls(calls)).toEqual([
469+
{ line: 0, method: "get_feature_flag", key: "my-flag" },
470+
]);
471+
});
472+
});
473+
474+
describe("Python — findInitCalls", () => {
475+
test("detects positional constructor Posthog('phc_token')", async () => {
476+
const code = `Posthog('phc_token')`;
477+
const inits = await detector.findInitCalls(code, "python");
478+
expect(inits).toHaveLength(1);
479+
expect(inits[0].token).toBe("phc_token");
480+
});
481+
482+
test("detects keyword constructor with api_key and host", async () => {
483+
const code = `Posthog(api_key='phc_token', host='https://app.posthog.com')`;
484+
const inits = await detector.findInitCalls(code, "python");
485+
expect(inits).toHaveLength(1);
486+
expect(inits[0].token).toBe("phc_token");
487+
expect(inits[0].apiHost).toBe("https://app.posthog.com");
488+
});
489+
});
490+
491+
// ═══════════════════════════════════════════════════
492+
// Go — additional findPostHogCalls / findInitCalls
493+
// ═══════════════════════════════════════════════════
494+
495+
describe("Go — findPostHogCalls (capture & flags)", () => {
496+
test("detects struct-based Enqueue capture", async () => {
497+
const code = [
498+
`package main`,
499+
``,
500+
`func main() {`,
501+
` client.Enqueue(posthog.Capture{Event: "purchase"})`,
502+
`}`,
503+
].join("\n");
504+
const calls = await detector.findPostHogCalls(code, "go");
505+
const capture = calls.find(
506+
(c) => c.method === "capture" && c.key === "purchase",
507+
);
508+
expect(capture).toBeDefined();
509+
});
510+
511+
test("detects flag method GetFeatureFlag", async () => {
512+
const code = [
513+
`package main`,
514+
``,
515+
`func main() {`,
516+
` client.GetFeatureFlag(posthog.FeatureFlagPayload{Key: "my-flag"})`,
517+
`}`,
518+
].join("\n");
519+
const calls = await detector.findPostHogCalls(code, "go");
520+
const flag = calls.find(
521+
(c) => c.method === "GetFeatureFlag" && c.key === "my-flag",
522+
);
523+
expect(flag).toBeDefined();
524+
});
525+
});
526+
527+
describe("Go — findInitCalls", () => {
528+
test("detects posthog.New constructor", async () => {
529+
const code = [
530+
`package main`,
531+
``,
532+
`func main() {`,
533+
` client := posthog.New("phc_token")`,
534+
`}`,
535+
].join("\n");
536+
const inits = await detector.findInitCalls(code, "go");
537+
expect(inits).toHaveLength(1);
538+
expect(inits[0].token).toBe("phc_token");
539+
});
540+
541+
test("detects posthog.NewWithConfig constructor", async () => {
542+
const code = [
543+
`package main`,
544+
``,
545+
`func main() {`,
546+
` client, _ := posthog.NewWithConfig("phc_token", posthog.Config{Endpoint: "https://app.posthog.com"})`,
547+
`}`,
548+
].join("\n");
549+
const inits = await detector.findInitCalls(code, "go");
550+
expect(inits).toHaveLength(1);
551+
expect(inits[0].token).toBe("phc_token");
552+
expect(inits[0].apiHost).toBe("https://app.posthog.com");
553+
});
554+
});
555+
556+
// ═══════════════════════════════════════════════════
557+
// Ruby — additional findPostHogCalls / findInitCalls
558+
// ═══════════════════════════════════════════════════
559+
560+
describe("Ruby — findPostHogCalls (capture & flags)", () => {
561+
test("detects capture with keyword args", async () => {
562+
const code = `client.capture(distinct_id: 'user', event: 'purchase')`;
563+
const calls = await detector.findPostHogCalls(code, "ruby");
564+
const capture = calls.find(
565+
(c) => c.method === "capture" && c.key === "purchase",
566+
);
567+
expect(capture).toBeDefined();
568+
});
569+
570+
test("detects flag method get_feature_flag", async () => {
571+
const code = `client.get_feature_flag('my-flag')`;
572+
const calls = await detector.findPostHogCalls(code, "ruby");
573+
const flag = calls.find(
574+
(c) => c.method === "get_feature_flag" && c.key === "my-flag",
575+
);
576+
expect(flag).toBeDefined();
577+
});
578+
});
579+
580+
describe("Ruby — findInitCalls", () => {
581+
test("detects PostHog::Client.new constructor", async () => {
582+
const code = `client = PostHog::Client.new(api_key: 'phc_token')`;
583+
const inits = await detector.findInitCalls(code, "ruby");
584+
expect(inits).toHaveLength(1);
585+
expect(inits[0].token).toBe("phc_token");
586+
});
587+
});
588+
589+
// ═══════════════════════════════════════════════════
590+
// Negative / edge cases
591+
// ═══════════════════════════════════════════════════
592+
593+
describe("Negative / edge cases", () => {
594+
test("unsupported language returns empty arrays", async () => {
595+
const code = `posthog.capture('event')`;
596+
const calls = await detector.findPostHogCalls(code, "haskell");
597+
const inits = await detector.findInitCalls(code, "haskell");
598+
expect(calls).toEqual([]);
599+
expect(inits).toEqual([]);
600+
});
601+
602+
test("non-PostHog client names are ignored", async () => {
603+
const code = `other.capture('event')`;
604+
605+
const jsCalls = await detector.findPostHogCalls(code, "javascript");
606+
const pyCalls = await detector.findPostHogCalls(code, "python");
607+
const rbCalls = await detector.findPostHogCalls(code, "ruby");
608+
609+
expect(jsCalls).toEqual([]);
610+
expect(pyCalls).toEqual([]);
611+
expect(rbCalls).toEqual([]);
612+
});
613+
});
450614
});

packages/enricher/src/parser-manager.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export class ParserManager {
1010
private parser: Parser | null = null;
1111
private languages = new Map<string, Parser.Language>();
1212
private queryCache = new Map<string, Parser.Query>();
13+
private maxCacheSize = 256;
1314
private initPromise: Promise<void> | null = null;
1415
private wasmDir = "";
1516
config: DetectionConfig = DEFAULT_CONFIG;
@@ -91,11 +92,21 @@ export class ParserManager {
9192
const cacheKey = `${lang.toString()}:${queryStr}`;
9293
let query = this.queryCache.get(cacheKey);
9394
if (query) {
95+
// LRU: move to end by deleting and re-inserting
96+
this.queryCache.delete(cacheKey);
97+
this.queryCache.set(cacheKey, query);
9498
return query;
9599
}
96100

97101
try {
98102
query = lang.query(queryStr);
103+
// Evict oldest entry if at capacity
104+
if (this.queryCache.size >= this.maxCacheSize) {
105+
const oldest = this.queryCache.keys().next().value;
106+
if (oldest !== undefined) {
107+
this.queryCache.delete(oldest);
108+
}
109+
}
99110
this.queryCache.set(cacheKey, query);
100111
return query;
101112
} catch (err) {

packages/enricher/tsup.config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { defineConfig } from "tsup";
22

33
export default defineConfig({
4-
entry: ["src/index.ts"],
4+
entry: [
5+
"src/index.ts",
6+
"src/flag-classification.ts",
7+
"src/stale-flags.ts",
8+
"src/types.ts",
9+
],
510
format: ["esm"],
611
dts: true,
712
sourcemap: true,

0 commit comments

Comments
 (0)