diff --git a/garden-service/test/integ-helpers.ts b/garden-service/test/integ-helpers.ts index a1b9835c08..1f36fc69e5 100644 --- a/garden-service/test/integ-helpers.ts +++ b/garden-service/test/integ-helpers.ts @@ -1,5 +1,4 @@ import * as execa from "execa" -import * as Bluebird from "bluebird" import * as mlog from "mocha-logger" import { remove } from "fs-extra" import { get, intersection } from "lodash" @@ -14,14 +13,12 @@ import { systemMetadataNamespace } from "../src/plugins/kubernetes/system" export const parsedArgs = parseArgs(process.argv.slice(2)) -export async function removeExampleDotGardenDirs() { - await Bluebird.map(Object.values(getExampleProjects()), async (projectRoot) => { - try { - await remove(resolve(projectRoot, DEFAULT_GARDEN_DIR_NAME)) - } catch (error) { - // No .garden directory found in projectRoot, so there's nothing to do here. - } - }) +export async function removeExampleDotGardenDir(projectRoot: string) { + try { + await remove(resolve(projectRoot, DEFAULT_GARDEN_DIR_NAME)) + } catch (error) { + // No .garden directory found in projectRoot, so there's nothing to do here. + } } export async function deleteExampleNamespaces(projectNames?: string[]) { @@ -59,7 +56,7 @@ export async function getAllNamespacesKubectl() { export async function deleteNamespacesKubectl(namespaces: string[]) { if (namespaces.length > 0) { - await execa("kubectl", ["delete", "ns", ...namespaces]) + await execa("kubectl", ["delete", "--wait=false", "ns", ...namespaces]) } } diff --git a/garden-service/test/integ/README.md b/garden-service/test/integ/README.md index becdce1509..48069b67d6 100644 --- a/garden-service/test/integ/README.md +++ b/garden-service/test/integ/README.md @@ -15,5 +15,5 @@ The `integ-full` script supports the following options: For example: ``` -npm run integ-full -- --binPath=/some/path/garden-bin --only=tasks +npm run integ-full -- --binPath=/some/path/garden-bin --project=tasks ``` \ No newline at end of file diff --git a/garden-service/test/integ/garden.yml b/garden-service/test/integ/garden.yml index 4d02beab79..bf2faa66d7 100644 --- a/garden-service/test/integ/garden.yml +++ b/garden-service/test/integ/garden.yml @@ -16,16 +16,16 @@ name: integ-tests type: exec tests: - name: demo-project - command: [npm, run, integ-full, --, --only=demo-project, --showlog=true, --env=testing] + command: [npm, run, integ-full, --, --project=demo-project, --showlog=true, --env=testing] - name: tasks # Tests for tasks are currently being skipped - command: [npm, run, integ-full, --, --only=tasks, --showlog=true, --env=testing] + command: [npm, run, integ-full, --, --project=tasks, --showlog=true, --env=testing] - name: hot-reload # Tests for hot-reload are currently being skipped - command: [npm, run, integ-full, --, --only=hot-reload, --showlog=true, --env=testing] + command: [npm, run, integ-full, --, --project=hot-reload, --showlog=true, --env=testing] - name: project-variables - command: [npm, run, integ-full, --, --only=project-variables, --showlog=true, --env=testing] + command: [npm, run, integ-full, --, --project=project-variables, --showlog=true, --env=testing] - name: vote-helm - command: [npm, run, integ-full, --, --only=vote-helm, --showlog=true, --env=testing] + command: [npm, run, integ-full, --, --project=vote-helm, --showlog=true, --env=testing] - name: vote - command: [npm, run, integ-full, --, --only=vote, --showlog=true, --env=testing] + command: [npm, run, integ-full, --, --project=vote, --showlog=true, --env=testing] - name: remote-sources - command: [npm, run, integ-full, --, --only=remote-sources, --showlog=true, --env=testing] + command: [npm, run, integ-full, --, --project=remote-sources, --showlog=true, --env=testing] diff --git a/garden-service/test/integ/integ-full.ts b/garden-service/test/integ/integ-full.ts index 9fa8f19661..e0021a6f9d 100755 --- a/garden-service/test/integ/integ-full.ts +++ b/garden-service/test/integ/integ-full.ts @@ -20,18 +20,25 @@ of the one at ${chalk.blue("[garden-root]/bin/garden")}. ${chalk.green("--env")}: The environment to run the test in. \ E.g. ${chalk.blue("local")} or ${chalk.blue("testing")}. -${chalk.green("--only")}: Runs only the test sequence indicated. \ +${chalk.green("--project")}: Specify the project to run (required). \ E.g. ${chalk.blue("demo-project")} or ${chalk.blue("vote-helm")}. -Example: ./garden-service/bin/integ-full.ts --binPath=/path/to/garden --only=demo-project +Example: ./garden-service/bin/integ-full.ts --binPath=/path/to/garden --project=demo-project ` async function run() { + const project = parsedArgs.project + + if (!project) { + throw new Error(`Must specify project name with --project parameter`) + } + // Abort if examples dir is dirty to prevent changes being checked out + const projectDir = resolve(examplesDir, project) try { - await execa("git", ["diff-index", "--quiet", "HEAD", examplesDir]) + await execa("git", ["diff-index", "--quiet", "HEAD", projectDir]) } catch (_error) { - throw new InternalError("Examples directory is dirty. Aborting.", {}) + throw new InternalError(`${project} example directory is dirty. Aborting.`, {}) } if (parsedArgs["h"]) { @@ -46,7 +53,7 @@ async function run() { const mochaOpts = ["--opts", "test/mocha.integ.opts"] - for (const opt of ["binPath", "only", "env"]) { + for (const opt of ["binPath", "project", "env"]) { if (parsedArgs[opt]) { mochaOpts.push(`--${opt}`, parsedArgs[opt]) } diff --git a/garden-service/test/integ/src/pre-release.ts b/garden-service/test/integ/src/pre-release.ts index da570c3470..079a336c02 100644 --- a/garden-service/test/integ/src/pre-release.ts +++ b/garden-service/test/integ/src/pre-release.ts @@ -17,67 +17,55 @@ import { deleteExampleNamespaces, parsedArgs, searchLog, - removeExampleDotGardenDirs, + removeExampleDotGardenDir, } from "../../integ-helpers" -type ProjectName = - "demo-project" | - "hot-reload" | - "hello-world" | - "tasks" | - "vote" | - "vote-helm" | - "remote-sources" - -const prereleaseSequences: ProjectName[] = [ - "demo-project", - "hot-reload", - "hello-world", - "tasks", - "vote", - "vote-helm", - "remote-sources", -] +// TODO: Add test for verifying that CLI returns with an error when called with an unknown command +describe("PreReleaseTests", () => { + // We assume tests are running remotely in CI if env is passed, otherwise locally. + const env = parsedArgs["env"] + const project = parsedArgs["project"] -export const sequencesToRun = parsedArgs["only"] ? [parsedArgs["only"]] : prereleaseSequences -// We assume tests are running remotely in CI if env is passed, otherwise locally. -const env = parsedArgs["env"] + if (!project) { + throw new Error(`Must specify project name with --project parameter`) + } -export function getProjectNamespace(project: ProjectName) { - return `${project}-testing-${process.env.CIRCLE_BUILD_NUM || "default"}` -} + function getProjectNamespaces() { + const ns = `${project}-testing-${process.env.CIRCLE_BUILD_NUM || "default"}` + return [ns, ns + "--metadata"] + } -async function runWithEnv(project: ProjectName, command: string[]) { - const dir = resolve(examplesDir, project) - if (env) { - command.push("--env", env) + async function runWithEnv(command: string[]) { + const dir = resolve(examplesDir, project) + if (env) { + command.push("--env", env) + } + return runGarden(dir, command) } - return runGarden(dir, command) -} -function watchWithEnv(project: ProjectName, command: string[]) { - const dir = resolve(examplesDir, project) - if (env) { - command.push("--env", env) + function watchWithEnv(command: string[]) { + const dir = resolve(examplesDir, project) + if (env) { + command.push("--env", env) + } + return new GardenWatch(dir, command) } - return new GardenWatch(dir, command) -} -async function initIfRemote(project: ProjectName) { - // Assume env is remote if passed as arg - if (env) { - mlog.log("initing project", project) - await runWithEnv(project, ["init"]) + async function initIfRemote() { + // Assume env is remote if passed as arg + if (env) { + mlog.log("initing project", project) + await runWithEnv(["init"]) + } } -} -// TODO: Add test for verifying that CLI returns with an error when called with an unknown command -describe("PreReleaseTests", () => { - const namespaces = sequencesToRun.map(p => getProjectNamespace(p)) + const namespaces = getProjectNamespaces() + const projectPath = resolve(examplesDir, project) before(async () => { - mlog.log("deleting .garden folders") - await removeExampleDotGardenDirs() + mlog.log("deleting .garden folder") + await removeExampleDotGardenDir(projectPath) + await initIfRemote() }) after(async () => { @@ -94,38 +82,39 @@ describe("PreReleaseTests", () => { } }) - if (sequencesToRun.includes("demo-project")) { - describe("demo-project: top-level sanity checks", () => { - const demoProjectPath = resolve(examplesDir, "demo-project") - - before(async () => { - await initIfRemote("demo-project") - }) + describe("top-level sanity checks", () => { + it("runs the validate command", async () => { + await runWithEnv(["validate"]) + }) - it("runs the validate command", async () => { - await runWithEnv("demo-project", ["validate"]) - }) + it("runs the build command", async () => { + const logEntries = await runWithEnv(["build", "--force"]) + expect(searchLog(logEntries, /Done!/), "expected to find 'Done!' in log output").to.eql("passed") + }) - it("runs the deploy command", async () => { - const logEntries = await runWithEnv("demo-project", ["deploy"]) - expect(searchLog(logEntries, /Done!/), "expected to find 'Done!' in log output").to.eql("passed") - }) + it("runs the deploy command", async () => { + const logEntries = await runWithEnv(["deploy"]) + expect(searchLog(logEntries, /Done!/), "expected to find 'Done!' in log output").to.eql("passed") + }) - it("runs the test command", async () => { - const logEntries = await runWithEnv("demo-project", ["test"]) - expect(searchLog(logEntries, /Done!/), "expected to find 'Done!' in log output").to.eql("passed") - }) + it("runs the test command", async () => { + const logEntries = await runWithEnv(["test"]) + expect(searchLog(logEntries, /Done!/), "expected to find 'Done!' in log output").to.eql("passed") + }) + }) + if (project === "demo-project") { + describe("demo-project: top-level sanity checks", () => { it("runs the dev command", async () => { - const gardenWatch = watchWithEnv("demo-project", ["dev"]) + const gardenWatch = watchWithEnv(["dev"]) const testSteps = [ taskCompletedStep("deploy.backend", 1), waitingForChangesStep(), - changeFileStep(resolve(demoProjectPath, "backend/webserver/main.go"), + changeFileStep(resolve(projectPath, "backend/webserver/main.go"), "change app code in backend service"), taskCompletedStep("deploy.backend", 2), - changeFileStep(resolve(demoProjectPath, "backend/garden.yml"), + changeFileStep(resolve(projectPath, "backend/garden.yml"), "change garden.yml in backend service"), commandReloadedStep(), ] @@ -135,7 +124,7 @@ describe("PreReleaseTests", () => { }) } - if (sequencesToRun.includes("tasks")) { + if (project === "tasks") { /* * TODO: Re-enable once this has been debugged: * @@ -143,28 +132,19 @@ describe("PreReleaseTests", () => { * Are you missing a .transacting(trx) call? */ describe.skip("tasks", () => { - before(async () => { - await initIfRemote("tasks") - }) - - it("runs the deploy command", async () => { - const logEntries = await runWithEnv("tasks", ["deploy"]) - expect(searchLog(logEntries, /Done!/), "expected to find 'Done!' in log output").to.eql("passed") - }) - it("calls the hello service to fetch the usernames populated by the ruby migration", async () => { /** * Verify that the output includes the usernames populated by the ruby-migration task. * The users table was created by the node-migration task. */ - const logEntries = await runWithEnv("tasks", ["call", "hello"]) + const logEntries = await runWithEnv(["call", "hello"]) expect(searchLog(logEntries, /John, Paul, George, Ringo/), "expected to find populated usernames in log output") .to.eql("passed") }) }) } - if (sequencesToRun.includes("hot-reload")) { + if (project === "hot-reload") { /* * TODO: Re-enable once this has been debugged: * @@ -172,10 +152,9 @@ describe("PreReleaseTests", () => { * choose one of: [node-service garden-rsync] or one of the init containers: [garden-sync-init] */ describe.skip("hot-reload", () => { - it("runs the dev command with hot reloading enabled", async () => { const hotReloadProjectPath = resolve(examplesDir, "hot-reload") - const gardenWatch = watchWithEnv("hot-reload", ["dev", "--hot=node-service"]) + const gardenWatch = watchWithEnv(["dev", "--hot=node-service"]) const testSteps = [ dashboardUpStep(), @@ -192,32 +171,25 @@ describe("PreReleaseTests", () => { { description: "node-service returns the updated response text", condition: async () => { - const callLogEntries = await runWithEnv("hot-reload", ["call", "node-service"]) + const callLogEntries = await runWithEnv(["call", "node-service"]) return searchLog(callLogEntries, /Hello from Edge/) }, }, ] await gardenWatch.run({ testSteps }) - }) }) } - if (sequencesToRun.includes("vote-helm")) { + if (project === "vote-helm") { describe("vote-helm: helm & dependency calculations", () => { - const voteHelmProjectPath = resolve(examplesDir, "vote-helm") - - before(async () => { - await initIfRemote("vote-helm") - }) - it("runs the dev command", async () => { - const gardenWatch = watchWithEnv("vote-helm", ["dev"]) + const gardenWatch = watchWithEnv(["dev"]) const testSteps = [ waitingForChangesStep(), - changeFileStep(resolve(voteHelmProjectPath, "api-image/app.py"), "change api-image/app.py"), + changeFileStep(resolve(projectPath, "api-image/app.py"), "change api-image/app.py"), taskCompletedStep("build.api-image", 2), taskCompletedStep("build.api", 2), taskCompletedStep("deploy.api", 2), @@ -230,20 +202,14 @@ describe("PreReleaseTests", () => { }) } - if (sequencesToRun.includes("vote")) { + if (project === "vote") { describe("vote: dependency calculations", () => { - const voteProjectPath = resolve(examplesDir, "vote") - - before(async () => { - await initIfRemote("vote") - }) - it("runs the dev command", async () => { - const gardenWatch = watchWithEnv("vote", ["dev"]) + const gardenWatch = watchWithEnv(["dev"]) const testSteps = [ waitingForChangesStep(), - changeFileStep(resolve(voteProjectPath, "services/api/app.py"), "change services/api/app.py"), + changeFileStep(resolve(projectPath, "services/api/app.py"), "change services/api/app.py"), taskCompletedStep("build.api", 2), taskCompletedStep("deploy.api", 2), taskCompletedStep("deploy.vote", 2), @@ -254,23 +220,13 @@ describe("PreReleaseTests", () => { }) } - if (sequencesToRun.includes("remote-sources")) { + if (project === "remote-sources") { describe("remote sources", () => { - before(async () => { - await initIfRemote("remote-sources") - }) - - it("runs the deploy command", async () => { - const logEntries = await runWithEnv("remote-sources", ["deploy"]) - expect(searchLog(logEntries, /Done!/), "expected to find 'Done!' in log output").to.eql("passed") - }) - it("calls the result service to get a 200 OK response including the HTML for the result page", async () => { - const logEntries = await runWithEnv("remote-sources", ["call", "result"]) + const logEntries = await runWithEnv(["call", "result"]) expect(searchLog(logEntries, /200 OK/), "expected to find '200 OK' in log output").to.eql("passed") expect(searchLog(logEntries, /Cats/), "expected to find 'Cats' in log output").to.eql("passed") }) }) } - })