diff --git a/src/meta-cli/src/deploy/actors/task_manager.rs b/src/meta-cli/src/deploy/actors/task_manager.rs index 0c495593c..c59d8ee4b 100644 --- a/src/meta-cli/src/deploy/actors/task_manager.rs +++ b/src/meta-cli/src/deploy/actors/task_manager.rs @@ -284,16 +284,14 @@ impl Actor for TaskManager { fn stopping(&mut self, ctx: &mut Self::Context) -> Running { match &self.stop_reason { - Some(reason) => { - if matches!(reason, StopReason::Restart) { - self.watcher_addr = self - .init_params - .start_source(ctx.address(), self.task_generator.clone()); - Running::Continue - } else { - Running::Stop - } + Some(StopReason::Restart) => { + self.stop_reason.take(); + self.watcher_addr = self + .init_params + .start_source(ctx.address(), self.task_generator.clone()); + Running::Continue } + Some(_) => Running::Stop, None => Running::Continue, } } diff --git a/tests/e2e/cli/watch_test.ts b/tests/e2e/cli/watch_test.ts index e0c58c7b9..f28872250 100644 --- a/tests/e2e/cli/watch_test.ts +++ b/tests/e2e/cli/watch_test.ts @@ -2,75 +2,144 @@ // SPDX-License-Identifier: MPL-2.0 import * as path from "@std/path"; +import { assert } from "@std/assert"; import { Meta } from "test-utils/mod.ts"; import { MetaTest } from "test-utils/test.ts"; -import { killProcess, Lines } from "test-utils/process.ts"; +import { makeMetaCliTest } from "test-utils/meta.ts"; const typegraphConfig = ` typegraphs: typescript: - include: "api/example.ts"`; + include: "api/deps.ts"`; async function setupDirectory(t: MetaTest, dir: string) { + console.log("Preparing test directory..."); + await t.shell([ "bash", "-c", ` rm -rf ./tmp && mkdir -p tmp/deps - meta new --template deno ${dir} - cp ./e2e/cli/typegraphs/deps.ts ${path.join(dir, "api", "example.ts")} + meta new --template python ${dir} + cp ./e2e/cli/typegraphs/deps.ts ${path.join(dir, "api")} + cp ./e2e/cli/typegraphs/deps.ts ${path.join(dir, "api", "excluded.ts")} cp ./e2e/cli/artifacts/ops.ts ${path.join(dir, "deps", "ops.ts")} echo "${typegraphConfig}" >> ${path.join(dir, "metatype.yaml")} `, ]); } -Meta.test({ name: "meta dev: watch artifacts" }, async (t) => { +Meta.test({ name: "meta dev: watch typegraphs" }, async (t) => { const targetDir = path.join(t.workingDir, "tmp"); - console.log("Preparing test directory..."); - await setupDirectory(t, targetDir); - const metadev = new Deno.Command("meta", { - cwd: targetDir, - args: ["dev", `--gate=http://localhost:${t.port}`], - stderr: "piped", - }).spawn(); + const { expectStderr, stderr } = makeMetaCliTest(t, targetDir, [ + "dev", + `--gate=http://localhost:${t.port}`, + ]); + + await t.should("deploy typegraphs", async () => { + // order is not deterministic but there should be two + await expectStderr("successfully deployed typegraph"); + await expectStderr("successfully deployed typegraph"); + }); - const stderr = new Lines(metadev.stderr); + await t.should("watch modified typegraph", async () => { + await t.shell(["bash", "-c", "echo '' >> api/example.py"], { + currentDir: targetDir, + }); - await t.should("upload artifact", async () => { - await stderr.readWhile((line) => !line.includes("artifact uploaded")); + await expectStderr('File modified: "api/example.py"'); }); - await t.should("deploy typegraph", async () => { - await stderr.readWhile( - (line) => !line.includes("successfully deployed typegraph") - ); + await t.should("re-deploy typegraph", () => + expectStderr("successfully deployed typegraph example"), + ); + + await t.should("ignore excluded typegraph", async () => { + await t.shell(["bash", "-c", "echo '' >> api/excluded.ts"], { + currentDir: targetDir, + }); + + const lines: string[] = []; + + try { + await stderr.readWhile((line) => { + lines.push(line); + return true; + }, 3000); + } catch (_) { + // timeout error + } finally { + assert(!lines.join("\n").includes("api/excluded.ts")); + } }); +}); + +Meta.test({ name: "meta dev: watch metatype.yaml" }, async (t) => { + const targetDir = path.join(t.workingDir, "tmp"); - await t.shell(["bash", "-c", "echo '' >> deps/ops.ts"], { - currentDir: targetDir, + const { expectStderr } = makeMetaCliTest(t, targetDir, [ + "dev", + `--gate=http://localhost:${t.port}`, + ]); + + await t.should("deploy typegraphs", async () => { + await expectStderr("successfully deployed typegraph"); + await expectStderr("successfully deployed typegraph"); }); - await t.should("watch modified file", async () => { - await stderr.readWhile((line) => !line.includes("File modified")); + await t.should("watch modified configuration", async () => { + await t.shell(["bash", "-c", "echo '' >> metatype.yaml"], { + currentDir: targetDir, + }); + + await expectStderr("metatype configuration file changed"); }); - await t.should("re-upload artifact", async () => { - await stderr.readWhile((line) => !line.includes("artifact uploaded")); + await t.should("start restart process", async () => { + await expectStderr("reloading all the typegraphs"); + await expectStderr("force stopping active tasks"); + await expectStderr("starting discovery"); }); - await t.should("re-deploy typegraph", async () => { - await stderr.readWhile( - (line) => !line.includes("successfully deployed typegraph") - ); + await t.should("re-deploy all typegraphs", async () => { + await expectStderr("successfully deployed typegraph"); + await expectStderr("successfully deployed typegraph"); }); +}); + +Meta.test({ name: "meta dev: watch artifacts" }, async (t) => { + const targetDir = path.join(t.workingDir, "tmp"); - t.addCleanup(async () => { - await stderr.close(); - await killProcess(metadev); - await t.shell(["rm", "-rf", targetDir]); + const { expectStderr } = makeMetaCliTest(t, targetDir, [ + "dev", + `--gate=http://localhost:${t.port}`, + ]); + + await t.should("upload artifact", () => + expectStderr("artifact uploaded: ../deps/ops.ts"), + ); + await t.should("deploy typegraph", () => + expectStderr("successfully deployed typegraph deps"), + ); + await t.should("watch modified artifact", async () => { + await t.shell(["bash", "-c", "echo '' >> deps/ops.ts"], { + currentDir: targetDir, + }); + + await expectStderr('File modified: "deps/ops.ts"'); + await expectStderr("-> api/deps.ts"); }); + + await t.should("re-upload artifact", () => + expectStderr("artifact uploaded: ../deps/ops.ts"), + ); + + await t.should("re-deploy typegraph", () => + expectStderr("successfully deployed typegraph deps"), + ); + + await t.shell(["rm", "-rf", targetDir]); }); diff --git a/tests/utils/meta.ts b/tests/utils/meta.ts index 203371adb..aeab511ad 100644 --- a/tests/utils/meta.ts +++ b/tests/utils/meta.ts @@ -1,6 +1,9 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 +import { $ } from "@david/dax"; +import { killProcess, Lines } from "./process.ts"; +import { MetaTest } from "./test.ts"; import { shell, ShellOptions, ShellOutput } from "./shell.ts"; // added to path in dev/test.ts @@ -15,10 +18,9 @@ export async function metaCli( first: string | ShellOptions, ...input: string[] ): Promise { - const res = - await (typeof first === "string" - ? shell([metaCliExe, first, ...input]) - : shell([metaCliExe, ...input], first)); + const res = await (typeof first === "string" + ? shell([metaCliExe, first, ...input]) + : shell([metaCliExe, ...input], first)); return res; } @@ -53,3 +55,31 @@ export async function serialize( const res = await shell(cmd); return res.stdout; } + +export function makeMetaCliTest(t: MetaTest, cwd: string, args: string[]) { + const meta = new Deno.Command("meta", { + cwd, + args, + stdout: "piped", + stderr: "piped", + }).spawn(); + + const stdout = new Lines(meta.stdout); + const stderr = new Lines(meta.stderr); + + const expectStdout = async (str: string) => { + await stdout.readWhile((line) => !$.stripAnsi(line).includes(str)); + }; + + const expectStderr = async (str: string) => { + await stderr.readWhile((line) => !$.stripAnsi(line).includes(str)); + }; + + t.addCleanup(async () => { + await stdout.close(); + await stderr.close(); + await killProcess(meta); + }); + + return { meta, stdout, stderr, expectStdout, expectStderr }; +}