Compare commits
2 Commits
python-uv
...
tests/flow
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc781f0c51 | ||
|
|
ce504178c6 |
11
backend/tests/api/README.md
Normal file
11
backend/tests/api/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Api integration tests
|
||||||
|
|
||||||
|
You'll need deno to be installed and a windmill api endpoint.
|
||||||
|
|
||||||
|
By default the tests use the local environment endpoint <http://localhost:8000>. You can override it using `BASE_URL` environment variable.
|
||||||
|
|
||||||
|
By default the api authentication uses default local env user credentials. You can override api token using `WM_TOKEN` environment variable.
|
||||||
|
|
||||||
|
## Running the api tests
|
||||||
|
|
||||||
|
`deno test --allow-env --allow-net`
|
||||||
9
backend/tests/api/deno.json
Normal file
9
backend/tests/api/deno.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"dev": "deno run --watch main.ts",
|
||||||
|
"test": "deno test --allow-env --allow-net"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"@std/assert": "jsr:@std/assert@1"
|
||||||
|
}
|
||||||
|
}
|
||||||
40
backend/tests/api/deno.lock
generated
Normal file
40
backend/tests/api/deno.lock
generated
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"version": "3",
|
||||||
|
"packages": {
|
||||||
|
"specifiers": {
|
||||||
|
"jsr:@std/assert": "jsr:@std/assert@1.0.6",
|
||||||
|
"jsr:@std/assert@1": "jsr:@std/assert@1.0.6",
|
||||||
|
"jsr:@std/internal@^1.0.4": "jsr:@std/internal@1.0.4"
|
||||||
|
},
|
||||||
|
"jsr": {
|
||||||
|
"@std/assert@1.0.6": {
|
||||||
|
"integrity": "1904c05806a25d94fe791d6d883b685c9e2dcd60e4f9fc30f4fc5cf010c72207",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/internal@^1.0.4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"@std/internal@1.0.4": {
|
||||||
|
"integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redirects": {
|
||||||
|
"https://cdn.skypack.dev/human-id@3.0.0": "https://cdn.skypack.dev/new/human-id@v3.0.0/dist=es2019",
|
||||||
|
"https://cdn.skypack.dev/new/human-id@v3.0.0/dist=es2019": "https://cdn.skypack.dev/error/build:human-id@v3.0.0-ktOXHm1YV4tQ5io95RTp",
|
||||||
|
"https://deno.land/x/nanoid/mod.ts": "https://deno.land/x/nanoid@v3.0.0/mod.ts"
|
||||||
|
},
|
||||||
|
"remote": {
|
||||||
|
"https://cdn.skypack.dev/error/build:human-id@v3.0.0-ktOXHm1YV4tQ5io95RTp": "54c50c8a33ab04f2a14c966809df583d6c59f0f8b549c270d1127afc8d4c652b",
|
||||||
|
"https://deno.land/x/nanoid@v3.0.0/customAlphabet.ts": "1cfd7cfd2f07ca8d78a7e7855fcc9f59abf01ef2a127484ef94328fadf940ead",
|
||||||
|
"https://deno.land/x/nanoid@v3.0.0/customRandom.ts": "af56e19038c891a4b4ef2be931554c27579bd407ee5bbea5cb64f6ee1347cbe3",
|
||||||
|
"https://deno.land/x/nanoid@v3.0.0/mod.ts": "3ead610e40c58d8fdca21d5da9ec661445a2b82526e19c34d05de5f90be8a1be",
|
||||||
|
"https://deno.land/x/nanoid@v3.0.0/nanoid.ts": "8d119bc89a0f34e7bbe0c2dbdc280d01753e431af553d189663492310a31085d",
|
||||||
|
"https://deno.land/x/nanoid@v3.0.0/random.ts": "4da71d5f72f2bfcc6a4ee79b5d4e72f48dcf4fe4c3835fd5ebab08b9f33cd598",
|
||||||
|
"https://deno.land/x/nanoid@v3.0.0/urlAlphabet.ts": "8b1511deb1ecb23c66202b6000dc10fb68f9a96b5550c6c8cef5009324793431"
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/assert@1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
80
backend/tests/api/main_test.ts
Normal file
80
backend/tests/api/main_test.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
|
||||||
|
import { assertEquals } from "jsr:@std/assert";
|
||||||
|
import type { FlowPreview } from "../../../cli/gen/index.ts";
|
||||||
|
import * as api from "../../../cli/gen/index.ts";
|
||||||
|
import { awaitJobCompletion, setup } from "./utils.ts";
|
||||||
|
|
||||||
|
const flowPreview: FlowPreview = {
|
||||||
|
"args": {
|
||||||
|
"a": 4,
|
||||||
|
"b": 9
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"id": "a",
|
||||||
|
"value": {
|
||||||
|
"type": "rawscript",
|
||||||
|
"content": "// import * as wmill from \"windmill-client\"\n\nexport async function main(a: number, b : number) {\n return a + b\n}\n",
|
||||||
|
"language": "bun",
|
||||||
|
"input_transforms": {
|
||||||
|
"a": {
|
||||||
|
"type": "javascript",
|
||||||
|
"expr": "flow_input.a"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "javascript",
|
||||||
|
"expr": "flow_input.b"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tag": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b",
|
||||||
|
"value": {
|
||||||
|
"type": "rawscript",
|
||||||
|
"content": "// import * as wmill from \"windmill-client\"\n\nexport async function main(n: number) {\n return n / 2.0\n}\n",
|
||||||
|
"language": "bun",
|
||||||
|
"input_transforms": {
|
||||||
|
"n": {
|
||||||
|
"type": "javascript",
|
||||||
|
"expr": "results.a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tag": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"path": "u/admin/beauteous_flow"
|
||||||
|
};
|
||||||
|
|
||||||
|
Deno.test("Run a flow preview and check its completion", async () => {
|
||||||
|
const { workspace } = await setup()
|
||||||
|
|
||||||
|
const id = await api.runFlowPreview({
|
||||||
|
workspace,
|
||||||
|
requestBody: flowPreview
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await awaitJobCompletion({ id, workspace, timeoutMs: 2000 });
|
||||||
|
assertEquals(result.result, 6.5);
|
||||||
|
assertEquals(result.canceled, false);
|
||||||
|
assertEquals(result.success, true);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("Run a flow preview - Immediately cancel and check cancellation status", async () => {
|
||||||
|
const { workspace } = await setup()
|
||||||
|
|
||||||
|
const id = await api.runFlowPreview({
|
||||||
|
workspace,
|
||||||
|
requestBody: flowPreview
|
||||||
|
});
|
||||||
|
|
||||||
|
await api.cancelQueuedJob({ id, workspace, requestBody: { reason: "test cancellation"}});
|
||||||
|
const result = await awaitJobCompletion({ id, workspace, timeoutMs: 200});
|
||||||
|
assertEquals(result.canceled, true);
|
||||||
|
assertEquals(result.success, false);
|
||||||
|
});
|
||||||
51
backend/tests/api/utils.ts
Normal file
51
backend/tests/api/utils.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { OpenAPI, createWorkspace, getJob, login } from "../../../cli/gen/index.ts";
|
||||||
|
|
||||||
|
// Setup method for each api test
|
||||||
|
export async function setup() {
|
||||||
|
await generateApiToken()
|
||||||
|
const workspace = await genTestWorkspace();
|
||||||
|
|
||||||
|
return { workspace }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateApiToken() {
|
||||||
|
// if WM_TOKEN env variable not set
|
||||||
|
if (!OpenAPI.TOKEN) {
|
||||||
|
const email = Deno.env.get("LOGIN_WM_EMAIL") || "admin@windmill.dev";
|
||||||
|
const password = Deno.env.get("LOGIN_WM_PASSWORD") || "changeme";
|
||||||
|
const token = await login({ requestBody: { email, password } });
|
||||||
|
OpenAPI.TOKEN = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure a workspace named "api-tests" is created
|
||||||
|
*/
|
||||||
|
async function genTestWorkspace() {
|
||||||
|
const id = "api-tests"
|
||||||
|
|
||||||
|
await createWorkspace({ requestBody: { id, name: id}}).catch(()=> null)
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns The job result
|
||||||
|
*/
|
||||||
|
export async function awaitJobCompletion(data: { workspace: string, id: string, timeoutMs: number }) {
|
||||||
|
const start = Date.now();
|
||||||
|
while (true) {
|
||||||
|
const result = await getJob(data).catch(()=> null);
|
||||||
|
|
||||||
|
|
||||||
|
if (!!result && result.type === "CompletedJob") {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Date.now() - start) > data.timeoutMs) {
|
||||||
|
throw new Error("Job did not finish before the specified timeout: " + data.timeoutMs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user