-
Notifications
You must be signed in to change notification settings - Fork 6
ENG-2040 Test the generic upsert route #1238
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
base: eng-2039-create-a-generic-upsert-route
Are you sure you want to change the base?
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,134 @@ | ||
| Feature: Concept upsert | ||
| User story: | ||
| * As a user of the Roam plugin | ||
| * Logged in through a given space's anonymous account | ||
| * I want to be able to upsert concepts to that space | ||
|
|
||
| Acceptance criteria: | ||
| * The upsert should succeed | ||
|
|
||
| Background: | ||
| Given the database is blank | ||
| And the user user1 opens the Roam plugin in space s1 | ||
|
|
||
| Scenario Outline: Calling the upsert steps together | ||
| When user user1 upserts this cross-app data to space s1: | ||
| """ | ||
| { | ||
| "nodeSchemas": [ | ||
| { | ||
| "label": "Claim", | ||
| "authorId": "user1", | ||
| "localId": "ns1", | ||
| "createdAt": "2000/01/01", | ||
| "modifiedAt": "2001/01/02" | ||
| } | ||
| ], | ||
| "relationTripleSchemas": [ | ||
| { | ||
| "authorId": "user1", | ||
| "localId": "r3s1", | ||
| "label": "supports", | ||
| "complement": "supportedBy", | ||
| "sourceType": "ns1", | ||
| "destinationType": "ns1", | ||
| "createdAt": "2000/01/01", | ||
| "modifiedAt": "2001/01/02" | ||
| } | ||
| ], | ||
| "nodes": [ | ||
| { | ||
| "title": "Some first claim", | ||
| "authorId": "user1", | ||
| "localId": "n1", | ||
| "nodeType": "ns1", | ||
| "createdAt": "2000/01/01", | ||
| "modifiedAt": "2001/01/02", | ||
| "content": { | ||
| "direct": { | ||
| "value": "Some text" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "title": "Some second claim", | ||
| "authorId": "user1", | ||
| "localId": "n2", | ||
| "nodeType": "ns1", | ||
| "createdAt": "2000/01/03", | ||
| "modifiedAt": "2001/01/04", | ||
| "content": { | ||
| "direct": { | ||
| "value": "Some subtext", | ||
| "embedding": { | ||
| "model": "openai_text_embedding_3_small_1536", | ||
| "value": [ | ||
| 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ], | ||
| "relations": [ | ||
| { | ||
| "name": "a supports relation", | ||
| "authorId": "user1", | ||
| "nodeType": "r3s1", | ||
| "createdAt": "2000/01/04", | ||
| "modifiedAt": "2001/01/05", | ||
| "source": "n1", | ||
| "destination": "n2" | ||
| } | ||
| ] | ||
| } | ||
| """ | ||
| Then a user logged in space s1 should see 5 Concept in the database | ||
| And a user logged in space s1 should see 2 Content in the database | ||
| And a user logged in space s1 should see 1 ContentEmbedding_openai_text_embedding_3_small_1536 in the database | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { spawn, execSync } from "node:child_process"; | ||
| import { spawn, execSync, ChildProcessByStdio } from "node:child_process"; | ||
| import { join, dirname } from "path"; | ||
| import type { Readable } from "node:stream"; | ||
|
|
||
| const scriptDir = dirname(__filename); | ||
| const projectRoot = join(scriptDir, ".."); | ||
|
|
@@ -16,58 +17,96 @@ if (process.env.SUPABASE_PROJECT_ID !== "test") { | |
| process.exit(2); | ||
| } | ||
|
|
||
| const serve = spawn("supabase", ["functions", "serve"], { | ||
| cwd: projectRoot, | ||
| detached: true, | ||
| stdio: ["ignore", "pipe", "inherit"], | ||
| }); | ||
| process.env["SUPABASE_USE_DB"] = "local"; | ||
|
|
||
| let resolveCallback: ((value: unknown) => void) | undefined = undefined; | ||
| let rejectCallback: ((reason: unknown) => void) | undefined = undefined; | ||
| let serveSuccess = false; | ||
| let timeoutClear: NodeJS.Timeout | undefined = undefined; | ||
| class ParallelServer { | ||
| process: ChildProcessByStdio<null, Readable, null>; | ||
| promise: Promise<boolean>; | ||
| serveSuccess = false; | ||
| resolveCallback: | ||
| | ((value: boolean | PromiseLike<boolean>) => void) | ||
| | undefined; | ||
| rejectCallback: ((reason: unknown) => void) | undefined; | ||
| timeoutClear: NodeJS.Timeout | undefined; | ||
| constructor({ | ||
| cmd, | ||
| args, | ||
| readySignal, | ||
| dir, | ||
| }: { | ||
| cmd: string; | ||
| args: string[]; | ||
| readySignal: string; | ||
| dir?: string; | ||
| }) { | ||
| this.process = spawn(cmd, args, { | ||
| cwd: dir || projectRoot, | ||
| detached: true, | ||
| stdio: ["ignore", "pipe", "inherit"], | ||
| }); | ||
| this.promise = new Promise((rsc, rjc) => { | ||
| this.resolveCallback = rsc; | ||
| this.rejectCallback = rjc; | ||
|
|
||
| const servingReady = new Promise((rsc, rjc) => { | ||
| resolveCallback = rsc; | ||
| rejectCallback = rjc; | ||
| // Add timeout | ||
|
Member
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. . |
||
| this.timeoutClear = setTimeout(() => { | ||
| rjc(new Error("Timeout waiting for functions to serve")); | ||
| }, 30000); // 30 second timeout | ||
|
Member
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. . |
||
| }); | ||
| this.process.stdout.on("data", (data: Buffer) => { | ||
| const output = data.toString(); | ||
| console.log(`stdout: ${output}`); | ||
| if (output.includes(readySignal)) { | ||
| console.log("Found serving functions"); | ||
| this.serveSuccess = true; | ||
| clearTimeout(this.timeoutClear); | ||
| if (this.resolveCallback === undefined) | ||
| throw new Error("did not get callback"); | ||
| this.resolveCallback(true); | ||
| } | ||
| }); | ||
| this.process.on("close", () => { | ||
| if (!this.serveSuccess && this.rejectCallback) | ||
| this.rejectCallback(new Error("serve closed without being ready")); | ||
| }); | ||
| this.process.on("error", (err) => { | ||
| if (this.rejectCallback) this.rejectCallback(err); | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| // Add timeout | ||
| timeoutClear = setTimeout(() => { | ||
| rjc(new Error("Timeout waiting for functions to serve")); | ||
| }, 30000); // 30 second timeout | ||
| const supabaseServer = new ParallelServer({ | ||
| cmd: "supabase", | ||
| args: ["functions", "serve"], | ||
| readySignal: "Serving functions ", | ||
| }); | ||
|
|
||
| serve.stdout.on("data", (data: Buffer) => { | ||
| const output = data.toString(); | ||
| console.log(`stdout: ${output}`); | ||
| if (output.includes("Serving functions ")) { | ||
| console.log("Found serving functions"); | ||
| serveSuccess = true; | ||
| clearTimeout(timeoutClear); | ||
| if (resolveCallback === undefined) throw new Error("did not get callback"); | ||
| resolveCallback(null); | ||
| } | ||
| }); | ||
| serve.on("close", () => { | ||
| if (!serveSuccess && rejectCallback) | ||
| rejectCallback(new Error("serve closed without being ready")); | ||
| }); | ||
| serve.on("error", (err) => { | ||
| if (rejectCallback) rejectCallback(err); | ||
| const nextServer = new ParallelServer({ | ||
| cmd: "npm", | ||
| args: ["run", "dev"], | ||
| readySignal: "Ready in ", | ||
| dir: join(scriptDir, "../../../apps/website"), | ||
| }); | ||
|
|
||
| const doTest = async () => { | ||
| try { | ||
| await servingReady; | ||
| await Promise.all([supabaseServer.promise, nextServer.promise]); | ||
| execSync("cucumber-js", { | ||
| cwd: projectRoot, | ||
| stdio: "inherit", | ||
| }); | ||
| // will throw on failure | ||
| } finally { | ||
| if (serve.pid) | ||
| if (supabaseServer.process.pid) | ||
| try { | ||
| process.kill(-supabaseServer.process.pid); | ||
| } catch (e) { | ||
| console.error("Could not kill the process"); | ||
| // maybe it just ended on its own. | ||
| } | ||
| if (nextServer.process.pid) | ||
|
Member
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. On Windows process.kill(-pid) returned ESRCH and left the detached child alive, leaking port 3000. |
||
| try { | ||
| process.kill(-serve.pid); | ||
| process.kill(-nextServer.process.pid); | ||
| } catch (e) { | ||
| console.error("Could not kill the process"); | ||
| // maybe it just ended on its own. | ||
|
|
@@ -78,10 +117,12 @@ const doTest = async () => { | |
| doTest() | ||
| .then(() => { | ||
| console.log("success"); | ||
| clearTimeout(timeoutClear); | ||
| clearTimeout(supabaseServer.timeoutClear); | ||
| clearTimeout(nextServer.timeoutClear); | ||
| }) | ||
| .catch((err) => { | ||
| console.error(err); | ||
| clearTimeout(timeoutClear); | ||
| clearTimeout(supabaseServer.timeoutClear); | ||
| clearTimeout(nextServer.timeoutClear); | ||
| process.exit(1); | ||
| }); | ||
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.
Should this match
CrossAppRelation?