-
Notifications
You must be signed in to change notification settings - Fork 173
fix: exclude hidden doubts from similarity matches (#1349) #1391
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 |
|---|---|---|
| @@ -1,3 +1,18 @@ | ||
| process.env.GROQ_API_KEY = "mock-groq-key"; | ||
|
|
||
| jest.mock("@/lib/ai/groq-client", () => ({ | ||
| groq: { | ||
| embeddings: { | ||
| create: jest.fn(), | ||
| }, | ||
| chat: { | ||
| completions: { | ||
| create: jest.fn(), | ||
| }, | ||
| }, | ||
| }, | ||
| })); | ||
|
|
||
| import { currentUser } from "@clerk/nextjs/server"; | ||
| import { NextResponse } from "next/server"; | ||
|
|
||
|
|
@@ -170,6 +185,42 @@ describe("Doubt similarity API endpoint", () => { | |
| }); | ||
| }); | ||
|
|
||
| it("excludes hidden doubts from candidate query", async () => { | ||
| let capturedWhereArg: any = null; | ||
| const trackingQuery: any = { | ||
| from: () => trackingQuery, | ||
| where: (arg: any) => { | ||
| capturedWhereArg = arg; | ||
| return trackingQuery; | ||
| }, | ||
| orderBy: () => trackingQuery, | ||
| limit: () => trackingQuery, | ||
| then: (resolve: any) => Promise.resolve(resolve([])), | ||
| }; | ||
| dbSelectMock.mockReturnValue(trackingQuery); | ||
|
Comment on lines
+189
to
+200
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. Suggestion: The new test also bypasses the primary semantic-search path because the mocked embedding call has no successful vector response and the tracking query resolves to an empty list. Consequently, the assertion verifies only the LLM fallback query and cannot catch hidden doubts returned by Severity Level: Major
|
||
|
|
||
| const req = new Request("http://localhost/api/doubts/check-similarity", { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ | ||
| content: "How does photosynthesis convert light into energy?", | ||
| }), | ||
| }); | ||
|
|
||
| const res = await POST(req); | ||
|
|
||
| expect(res.status).toBe(200); | ||
| expect(capturedWhereArg).not.toBeNull(); | ||
| const hasIsHidden = (expr: any): boolean => { | ||
| if (!expr || typeof expr !== "object") return false; | ||
| if (expr.name === "isHidden") return true; | ||
| if (expr.queryChunks) | ||
| return expr.queryChunks.some((c: any) => hasIsHidden(c)); | ||
| return false; | ||
| }; | ||
| expect(hasIsHidden(capturedWhereArg)).toBe(true); | ||
| }); | ||
|
|
||
| it("requires authentication for classroom similarity checks", async () => { | ||
| const req = new Request("http://localhost/api/doubts/check-similarity", { | ||
| method: "POST", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,4 +56,33 @@ describe("Embeddings & Vector Duplicate Utilities", () => { | |
| const duplicates = await findSemanticDuplicates({ content: "What is momentum?" }); | ||
| expect(duplicates).toEqual([]); | ||
| }); | ||
|
|
||
| it("findSemanticDuplicates excludes hidden doubts from candidate query", async () => { | ||
| const mockVector = new Array(1536).fill(0.1); | ||
| (groq.embeddings.create as jest.Mock).mockResolvedValueOnce({ | ||
| data: [{ embedding: mockVector }], | ||
| }); | ||
|
|
||
| const selectMock = db.select as jest.Mock; | ||
| const whereMock = jest.fn().mockReturnThis(); | ||
| selectMock.mockReturnValue({ | ||
| from: jest.fn().mockReturnThis(), | ||
| where: whereMock, | ||
| orderBy: jest.fn().mockReturnThis(), | ||
| limit: jest.fn().mockResolvedValue([]), | ||
| }); | ||
|
|
||
| await findSemanticDuplicates({ content: "What is momentum?" }); | ||
|
|
||
| expect(whereMock).toHaveBeenCalledTimes(1); | ||
| const whereArg = whereMock.mock.calls[0][0]; | ||
| const hasIsHidden = (expr: any): boolean => { | ||
| if (!expr || typeof expr !== "object") return false; | ||
| if (expr.name === "isHidden") return true; | ||
| if (expr.queryChunks) | ||
| return expr.queryChunks.some((c: any) => hasIsHidden(c)); | ||
| return false; | ||
| }; | ||
| expect(hasIsHidden(whereArg)).toBe(true); | ||
|
Comment on lines
+79
to
+86
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. Suggestion: The test checks only that some AST node is named Severity Level: Major
|
||
| }); | ||
| }); | ||
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.
Suggestion: The new test only reaches the fallback query: the embedding mock is never configured with a valid 1536-dimensional vector, so
findSemanticDuplicatesreturns an empty result before its database query is executed. A regression removing the hidden predicate from the primary vector-search path would therefore still pass this test. Configure the embedding response and capture/assert the vector query separately. [code quality]Severity Level: Major⚠️
Prompt for AI Agent 🤖