-
Notifications
You must be signed in to change notification settings - Fork 0
fix: address external review feedback (title-caps fact, testkit ZSet fidelity, mock-leak, doctor semver, replay seeding) #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| { | ||
| "route": "/internal/menu/undo-action", | ||
| "mod": true, | ||
| "body": { "location": "post", "targetId": "t3_target" }, | ||
| "redisZsets": { | ||
| "testsub:audit": [{ "member": "a_undo_demo", "score": 1730000000000 }] | ||
| }, | ||
| "redisHashes": { | ||
| "testsub:audit:a_undo_demo": { | ||
| "action": "remove", | ||
| "outcome": "applied", | ||
| "thingId": "t3_target", | ||
| "thingType": "post", | ||
| "authorName": "spammer", | ||
| "ruleSourceNL": "remove posts from accounts under 50 karma" | ||
| } | ||
| }, | ||
| "redis": { | ||
| "testsub:rollback:a_undo_demo": "{\"entry\":{\"actionId\":\"a_undo_demo\",\"ruleId\":\"r_low_karma\",\"ruleSourceNL\":\"remove posts from accounts under 50 karma\",\"thingId\":\"t3_target\",\"thingType\":\"post\",\"action\":\"remove\",\"params\":{},\"authorName\":\"spammer\",\"ts\":1730000000000,\"outcome\":\"applied\"},\"reverseParams\":{\"wasRemoved\":false,\"action\":\"remove\"}}" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -70,21 +70,22 @@ export function makeFakeRedis(): FakeRedis { | |||||||||
| }, | ||||||||||
| hGetAll: async (k: string) => ({ ...(hashes.get(k) ?? {}) }), | ||||||||||
| zAdd: async (k: string, entry: { member: string; score: number }) => { | ||||||||||
| const arr = zsets.get(k) ?? []; | ||||||||||
| // Real Redis ZADD updates the score of an existing member, not a duplicate. | ||||||||||
| const arr = (zsets.get(k) ?? []).filter((e) => e.member !== entry.member); | ||||||||||
| arr.push(entry); | ||||||||||
| zsets.set(k, arr); | ||||||||||
| }, | ||||||||||
| zRange: async (k: string, start: number, stop: number, opts?: { by?: 'rank' | 'score'; reverse?: boolean }) => { | ||||||||||
| let arr = [...(zsets.get(k) ?? [])].sort((a, b) => a.score - b.score); | ||||||||||
| const arr = [...(zsets.get(k) ?? [])].sort((a, b) => a.score - b.score); | ||||||||||
| if (opts?.reverse) arr.reverse(); | ||||||||||
| if (opts?.by === 'score') { | ||||||||||
| arr = arr.filter((e) => e.score >= start && e.score <= stop); | ||||||||||
| } else { | ||||||||||
| if (opts?.reverse) arr.reverse(); | ||||||||||
| const end = stop < 0 ? arr.length : stop + 1; | ||||||||||
| arr = arr.slice(start, end); | ||||||||||
| return arr; | ||||||||||
| // start/stop are score bounds (low..high); honour them regardless of order. | ||||||||||
| const [lo, hi] = start <= stop ? [start, stop] : [stop, start]; | ||||||||||
| return arr.filter((e) => e.score >= lo && e.score <= hi); | ||||||||||
| } | ||||||||||
| return arr; | ||||||||||
| // by rank (default): start/stop are indices into the (possibly reversed) array. | ||||||||||
| const end = stop < 0 ? arr.length : stop + 1; | ||||||||||
| return arr.slice(start, end); | ||||||||||
|
Comment on lines
+87
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current logic for handling negative
Suggested change
|
||||||||||
| }, | ||||||||||
| zCount: async (k: string, min: number, max: number | '+inf') => { | ||||||||||
| const hi = max === '+inf' ? Infinity : max; | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,7 +27,9 @@ type Fixture = { | |||||||||
| mod?: boolean; | ||||||||||
| settings?: Record<string, unknown>; | ||||||||||
| openai?: unknown; | ||||||||||
| redis?: Record<string, string>; | ||||||||||
| redis?: Record<string, string>; // string keys, e.g. `${sub}:rules:active` | ||||||||||
| redisHashes?: Record<string, Record<string, string>>; // hash keys, e.g. `${sub}:audit:<id>` | ||||||||||
| redisZsets?: Record<string, Array<{ member: string; score: number }>>; // zset keys, e.g. `${sub}:audit` | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| function snapshotRedisKeys() { | ||||||||||
|
|
@@ -48,11 +50,27 @@ describe.skipIf(!FIXTURE)('replay', () => { | |||||||||
| ); | ||||||||||
| const body = 'body' in fx && fx.body !== undefined ? fx.body : fx; | ||||||||||
|
|
||||||||||
| // Generic Reddit "thing" stub so action/rollback flows complete in a replay | ||||||||||
| // (tests use precise mocks; here we just want to observe the control flow). | ||||||||||
| const stubThing = { | ||||||||||
| approve: async () => {}, | ||||||||||
| unlock: async () => {}, | ||||||||||
| lock: async () => {}, | ||||||||||
| remove: async () => {}, | ||||||||||
| removed: false, | ||||||||||
| flair: null as { text: string } | null, | ||||||||||
| }; | ||||||||||
| fakeReddit.getPostById.mockResolvedValue(stubThing); | ||||||||||
| fakeReddit.getCommentById.mockResolvedValue(stubThing); | ||||||||||
|
Comment on lines
+63
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
Suggested change
|
||||||||||
|
|
||||||||||
| // Apply fixture-driven mock state. | ||||||||||
| if (fx.mod ?? true) fakeReddit.getModerators.mockResolvedValue(fakeListing([{ username: 'caller' }])); | ||||||||||
| if (fx.settings) fakeSettings.get.mockImplementation(async (k: string) => fx.settings![k]); | ||||||||||
| if (fx.openai !== undefined) fakeFetch.mockResolvedValue(openaiResponse(fx.openai)); | ||||||||||
| if (fx.redis) for (const [k, v] of Object.entries(fx.redis)) await fakeRedis.set(k, v); | ||||||||||
| if (fx.redisHashes) for (const [k, h] of Object.entries(fx.redisHashes)) await fakeRedis.hSet(k, h); | ||||||||||
| if (fx.redisZsets) | ||||||||||
| for (const [k, members] of Object.entries(fx.redisZsets)) for (const m of members) await fakeRedis.zAdd(k, m); | ||||||||||
|
|
||||||||||
| const before = snapshotRedisKeys(); | ||||||||||
| const res = await app.fetch( | ||||||||||
|
|
||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
upperCaseRatioOffunction is strictly limited to ASCII letters (A-Z,a-z). This means it will return a ratio of 0 for content written in non-Latin scripts (e.g., Cyrillic, Greek, or CJK), even if the text is in an uppercase equivalent. While this suffices for the current starter rules, it limits the tool's effectiveness for international subreddits. Consider using a Unicode-aware approach (e.g., checking Unicode categories) if global support is intended.