From e377ed4e98c975cc54952066b198ce2e1c53365f Mon Sep 17 00:00:00 2001 From: Jon Edvald Date: Mon, 20 Jan 2020 17:45:50 +0100 Subject: [PATCH] fix: added tests + fixed subtle issues in getModuleWatchTasks (TBS) --- garden-service/src/commands/deploy.ts | 1 - garden-service/src/commands/dev.ts | 1 - garden-service/src/config-graph.ts | 22 +- garden-service/src/tasks/helpers.ts | 87 ++-- garden-service/test/unit/src/config-graph.ts | 304 +++++++++----- garden-service/test/unit/src/tasks/helpers.ts | 390 +++++++++++++++++- 6 files changed, 629 insertions(+), 176 deletions(-) diff --git a/garden-service/src/commands/deploy.ts b/garden-service/src/commands/deploy.ts index c5b65f2dd47..ebf1dd63a2f 100644 --- a/garden-service/src/commands/deploy.ts +++ b/garden-service/src/commands/deploy.ts @@ -167,7 +167,6 @@ export class DeployCommand extends Command { graph, log, module, - serviceNames: module.serviceNames.filter((name) => serviceNames.includes(name)), hotReloadServiceNames, }) diff --git a/garden-service/src/commands/dev.ts b/garden-service/src/commands/dev.ts index 19c3979d75f..adb3c7e9187 100644 --- a/garden-service/src/commands/dev.ts +++ b/garden-service/src/commands/dev.ts @@ -189,7 +189,6 @@ export class DevCommand extends Command { log, graph: updatedGraph, module, - serviceNames: module.serviceNames, hotReloadServiceNames, }) diff --git a/garden-service/src/config-graph.ts b/garden-service/src/config-graph.ts index 830ace0a679..f71cef0d551 100644 --- a/garden-service/src/config-graph.ts +++ b/garden-service/src/config-graph.ts @@ -362,24 +362,20 @@ export class ConfigGraph { /** * Returns the set union of modules with the set union of their dependants (across all dependency types, recursively). */ - async withDependantModules(modules: Module[], filterFn?: DependencyRelationFilterFn): Promise { - const dependants = flatten(await Bluebird.map(modules, (m) => this.getDependantsForModule(m, filterFn))) + async withDependantModules(modules: Module[]): Promise { + const dependants = flatten(await Bluebird.map(modules, (m) => this.getDependantsForModule(m, true))) // We call getModules to ensure that the returned modules have up-to-date versions. const dependantModules = await this.modulesForRelations(await this.mergeRelations(...dependants)) return this.getModules({ names: uniq(modules.concat(dependantModules).map((m) => m.name)), includeDisabled: true }) } /** - * Returns all build and runtime dependants of module and its services & tasks (recursively). + * Returns all build and runtime dependants of a module and its services & tasks (recursively). + * Includes the services and tasks contained in the given module, but does _not_ contain the build node for the + * module itself. */ - async getDependantsForModule(module: Module, filterFn?: DependencyRelationFilterFn): Promise { - return this.mergeRelations( - ...(await Bluebird.all([ - this.getDependants({ nodeType: "build", name: module.name, recursive: true, filterFn }), - this.getDependantsForMany({ nodeType: "deploy", names: module.serviceNames, recursive: true, filterFn }), - this.getDependantsForMany({ nodeType: "run", names: module.taskNames, recursive: true, filterFn }), - ])) - ) + async getDependantsForModule(module: Module, recursive: boolean): Promise { + return this.getDependants({ nodeType: "build", name: module.name, recursive }) } /** @@ -535,8 +531,8 @@ export class ConfigGraph { private async relationsFromNames(names: DependencyRelationNames): Promise { return Bluebird.props({ build: this.getModules({ names: names.build, includeDisabled: true }), - deploy: this.getServices({ names: names.deploy }), - run: this.getTasks({ names: names.run }), + deploy: this.getServices({ names: names.deploy, includeDisabled: true }), + run: this.getTasks({ names: names.run, includeDisabled: true }), test: Object.values(pick(this.testConfigs, names.test)).map((t) => t.config), }) } diff --git a/garden-service/src/tasks/helpers.ts b/garden-service/src/tasks/helpers.ts index 1bd06111d49..c4bf9cda104 100644 --- a/garden-service/src/tasks/helpers.ts +++ b/garden-service/src/tasks/helpers.ts @@ -7,7 +7,7 @@ */ import Bluebird from "bluebird" -import { intersection, uniq, flatten } from "lodash" +import { intersection, uniq, flatten, uniqBy } from "lodash" import { DeployTask } from "./deploy" import { Garden } from "../garden" import { Module } from "../types/module" @@ -27,78 +27,62 @@ export async function getModuleWatchTasks({ log, graph, module, - serviceNames, hotReloadServiceNames, }: { garden: Garden log: LogEntry graph: ConfigGraph module: Module - serviceNames: string[] hotReloadServiceNames: string[] }): Promise { let buildTasks: BaseTask[] = [] - let dependantBuildModules: Module[] = [] - let servicesToDeploy: Service[] = [] - const hotReloadModuleNames = await getModuleNames(graph, hotReloadServiceNames) - - const dependantFilterFn = (dependantNode: DependencyGraphNode) => - !hotReloadModuleNames.includes(dependantNode.moduleName) - - if (intersection(module.serviceNames, hotReloadServiceNames).length) { - // Hot reloading is enabled for one or more of module's services. - const serviceDeps = await graph.getDependantsForMany({ - nodeType: "deploy", - names: module.serviceNames, - recursive: true, - filterFn: dependantFilterFn, - }) - - dependantBuildModules = serviceDeps.build - servicesToDeploy = serviceDeps.deploy - } else { - const dependants = await graph.getDependantsForModule(module, dependantFilterFn) + const dependants = await graph.getDependantsForModule(module, true) + if (intersection(module.serviceNames, hotReloadServiceNames).length === 0) { buildTasks = await BuildTask.factory({ garden, log, module, force: true, }) - - dependantBuildModules = dependants.build - servicesToDeploy = (await graph.getServices({ names: serviceNames })).concat(dependants.deploy) } const dependantBuildTasks = flatten( - await Bluebird.map(dependantBuildModules, (m) => - BuildTask.factory({ - garden, - log, - module: m, - force: false, - }) + await Bluebird.map( + dependants.build.filter((m) => !m.disabled), + (m) => + BuildTask.factory({ + garden, + log, + module: m, + force: false, + }) ) ) - const deployTasks = servicesToDeploy.map( - (service) => - new DeployTask({ - garden, - log, - graph, - service, - force: true, - forceBuild: false, - fromWatch: true, - hotReloadServiceNames, - }) - ) + const deployTasks = dependants.deploy + .filter((s) => !s.disabled && !hotReloadServiceNames.includes(s.name)) + .map( + (service) => + new DeployTask({ + garden, + log, + graph, + service, + force: true, + forceBuild: false, + fromWatch: true, + hotReloadServiceNames, + }) + ) - const hotReloadServices = await graph.getServices({ names: hotReloadServiceNames }) + const hotReloadServices = await graph.getServices({ names: hotReloadServiceNames, includeDisabled: true }) const hotReloadTasks = hotReloadServices - .filter((service) => service.module.name === module.name || service.sourceModule.name === module.name) + .filter( + (service) => + !service.disabled && (service.module.name === module.name || service.sourceModule.name === module.name) + ) .map((service) => new HotReloadTask({ garden, graph, log, service, force: true })) const outputTasks = [...buildTasks, ...dependantBuildTasks, ...deployTasks, ...hotReloadTasks] @@ -106,12 +90,7 @@ export async function getModuleWatchTasks({ log.silly(`getModuleWatchTasks called for module ${module.name}, returning the following tasks:`) log.silly(` ${outputTasks.map((t) => t.getKey()).join(", ")}`) - return outputTasks -} - -async function getModuleNames(dg: ConfigGraph, hotReloadServiceNames: string[]) { - const services = await dg.getServices({ names: hotReloadServiceNames }) - return uniq(services.map((s) => s.module.name)) + return uniqBy(outputTasks, (t) => t.getKey()) } export function makeTestTaskName(moduleName: string, testConfigName: string) { diff --git a/garden-service/test/unit/src/config-graph.ts b/garden-service/test/unit/src/config-graph.ts index 57d78048979..302f8b1d7f8 100644 --- a/garden-service/test/unit/src/config-graph.ts +++ b/garden-service/test/unit/src/config-graph.ts @@ -486,24 +486,22 @@ describe("ConfigGraph", () => { name: "foo", outputs: {}, path: tmpPath, - serviceConfigs: [ - { - name: "disabled-service", - dependencies: [], - disabled: true, - hotReloadable: false, - spec: {}, - }, - { - name: "enabled-service", - dependencies: ["disabled-service"], - disabled: true, - hotReloadable: false, - spec: {}, - }, - ], + serviceConfigs: [], taskConfigs: [], - spec: {}, + spec: { + services: [ + { + name: "disabled-service", + dependencies: [], + disabled: true, + }, + { + name: "enabled-service", + dependencies: ["disabled-service"], + disabled: true, + }, + ], + }, testConfigs: [], type: "test", }, @@ -532,25 +530,24 @@ describe("ConfigGraph", () => { name: "foo", outputs: {}, path: tmpPath, - serviceConfigs: [ - { - name: "enabled-service", - dependencies: ["disabled-task"], - disabled: true, - hotReloadable: false, - spec: {}, - }, - ], - taskConfigs: [ - { - name: "disabled-task", - dependencies: [], - disabled: true, - spec: {}, - timeout: null, - }, - ], - spec: {}, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "enabled-service", + dependencies: ["disabled-task"], + disabled: false, + }, + ], + tasks: [ + { + name: "disabled-task", + dependencies: [], + disabled: true, + }, + ], + }, testConfigs: [], type: "test", }, @@ -580,17 +577,17 @@ describe("ConfigGraph", () => { include: [], outputs: {}, path: tmpPath, - serviceConfigs: [ - { - name: "disabled-service", - dependencies: [], - disabled: true, - hotReloadable: false, - spec: {}, - }, - ], + serviceConfigs: [], taskConfigs: [], - spec: {}, + spec: { + services: [ + { + name: "disabled-service", + dependencies: [], + disabled: true, + }, + ], + }, testConfigs: [], type: "test", }, @@ -603,17 +600,17 @@ describe("ConfigGraph", () => { include: [], outputs: {}, path: tmpPath, - serviceConfigs: [ - { - name: "enabled-service", - dependencies: ["disabled-service"], - disabled: true, - hotReloadable: false, - spec: {}, - }, - ], + serviceConfigs: [], taskConfigs: [], - spec: {}, + spec: { + services: [ + { + name: "enabled-service", + dependencies: ["disabled-service"], + disabled: false, + }, + ], + }, testConfigs: [], type: "test", }, @@ -642,25 +639,24 @@ describe("ConfigGraph", () => { name: "foo", outputs: {}, path: tmpPath, - serviceConfigs: [ - { - name: "disabled-service", - dependencies: [], - disabled: true, - hotReloadable: false, - spec: {}, - }, - ], - taskConfigs: [ - { - name: "enabled-task", - dependencies: ["disabled-service"], - disabled: false, - spec: {}, - timeout: null, - }, - ], - spec: {}, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "disabled-service", + dependencies: [], + disabled: true, + }, + ], + tasks: [ + { + name: "enabled-task", + dependencies: ["disabled-service"], + disabled: false, + }, + ], + }, testConfigs: [], type: "test", }, @@ -689,26 +685,25 @@ describe("ConfigGraph", () => { name: "foo", outputs: {}, path: tmpPath, - serviceConfigs: [ - { - name: "disabled-service", - dependencies: [], - disabled: true, - hotReloadable: false, - spec: {}, - }, - ], + serviceConfigs: [], taskConfigs: [], - spec: {}, - testConfigs: [ - { - name: "enabled-test", - dependencies: ["disabled-service"], - disabled: false, - spec: {}, - timeout: null, - }, - ], + spec: { + services: [ + { + name: "disabled-service", + dependencies: [], + disabled: true, + }, + ], + tests: [ + { + name: "enabled-test", + dependencies: ["disabled-service"], + disabled: false, + }, + ], + }, + testConfigs: [], type: "test", }, ]) @@ -769,6 +764,127 @@ describe("ConfigGraph", () => { }) }) + describe("getDependants", () => { + it("should not traverse past disabled services", async () => { + const garden = await makeTestGardenA() + + garden.setModuleConfigs([ + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: false, + name: "module-a", + include: [], + outputs: {}, + path: tmpPath, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-a", + dependencies: [], + disabled: true, + }, + ], + }, + testConfigs: [], + type: "test", + }, + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: false, + name: "module-b", + include: [], + outputs: {}, + path: tmpPath, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-b", + dependencies: ["service-a"], + disabled: false, + }, + ], + }, + testConfigs: [], + type: "test", + }, + ]) + + const graph = await garden.getConfigGraph(garden.log) + const deps = await graph.getDependants({ nodeType: "build", name: "module-a", recursive: true }) + + expect(deps.deploy.map((m) => m.name)).to.eql(["service-a"]) + }) + }) + + describe("getDependantsForModule", () => { + it("should return services and tasks for a build dependant of the given module", async () => { + const garden = await makeTestGardenA() + + garden.setModuleConfigs([ + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: false, + name: "module-a", + include: [], + outputs: {}, + path: tmpPath, + serviceConfigs: [], + taskConfigs: [], + spec: {}, + testConfigs: [], + type: "test", + }, + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [{ name: "module-a", copy: [] }] }, + disabled: false, + name: "module-b", + include: [], + outputs: {}, + path: tmpPath, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-b", + dependencies: [], + disabled: false, + }, + ], + tasks: [ + { + name: "task-b", + dependencies: [], + disabled: false, + }, + ], + }, + testConfigs: [], + type: "test", + }, + ]) + + const graph = await garden.getConfigGraph(garden.log) + const moduleA = await graph.getModule("module-a") + const deps = await graph.getDependantsForModule(moduleA, true) + + expect(deps.deploy.map((m) => m.name)).to.eql(["service-b"]) + expect(deps.run.map((m) => m.name)).to.eql(["task-b"]) + }) + }) + describe("resolveDependencyModules", () => { it("should resolve build dependencies", async () => { const modules = await graphA.resolveDependencyModules([{ name: "module-c", copy: [] }], []) diff --git a/garden-service/test/unit/src/tasks/helpers.ts b/garden-service/test/unit/src/tasks/helpers.ts index 86415045bea..d87cb8312a6 100644 --- a/garden-service/test/unit/src/tasks/helpers.ts +++ b/garden-service/test/unit/src/tasks/helpers.ts @@ -2,25 +2,23 @@ import { expect } from "chai" import { uniq } from "lodash" import { resolve } from "path" import { Garden } from "../../../../src/garden" -import { makeTestGarden, dataDir } from "../../../helpers" +import { makeTestGarden, dataDir, makeTestGardenA } from "../../../helpers" import { getModuleWatchTasks } from "../../../../src/tasks/helpers" import { BaseTask } from "../../../../src/tasks/base" import { LogEntry } from "../../../../src/logger/log-entry" -import { ConfigGraph } from "../../../../src/config-graph" +import { DEFAULT_API_VERSION } from "../../../../src/constants" function sortedBaseKeys(tasks: BaseTask[]): string[] { return uniq(tasks.map((t) => t.getKey())).sort() } describe("TaskHelpers", () => { - let garden: Garden - let graph: ConfigGraph + let depGarden: Garden let log: LogEntry before(async () => { - garden = await makeTestGarden(resolve(dataDir, "test-project-dependants")) - graph = await garden.getConfigGraph(garden.log) - log = garden.log + depGarden = await makeTestGarden(resolve(dataDir, "test-project-dependants")) + log = depGarden.log }) /** @@ -28,6 +26,138 @@ describe("TaskHelpers", () => { * getDependencies methods of the task classes in question. */ describe("getModuleWatchTasks", () => { + it("should return no deploy tasks for a disabled module, but include its dependant tasks", async () => { + const garden = await makeTestGardenA() + + garden.setModuleConfigs([ + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: true, // <--------------- + name: "module-a", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-a", + dependencies: [], + disabled: false, + }, + ], + }, + testConfigs: [], + type: "test", + }, + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [{ name: "module-a", copy: [] }] }, + disabled: false, + name: "module-b", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-b", + dependencies: [], + disabled: false, + }, + ], + }, + testConfigs: [], + type: "test", + }, + ]) + + const graph = await garden.getConfigGraph(garden.log) + const module = await graph.getModule("module-a", true) + + const tasks = await getModuleWatchTasks({ + garden, + graph, + log, + module, + hotReloadServiceNames: [], + }) + + expect(sortedBaseKeys(tasks)).to.eql(["build.module-a", "build.module-b", "deploy.service-b"]) + }) + + it("should omit tasks for disabled dependant modules", async () => { + const garden = await makeTestGardenA() + + garden.setModuleConfigs([ + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: false, + name: "module-a", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-a", + dependencies: [], + disabled: false, + }, + ], + }, + testConfigs: [], + type: "test", + }, + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [{ name: "module-a", copy: [] }] }, + disabled: true, // <--------------- + name: "module-b", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-b", + dependencies: [], + disabled: false, + }, + ], + }, + testConfigs: [], + type: "test", + }, + ]) + + const graph = await garden.getConfigGraph(garden.log) + const module = await graph.getModule("module-a", true) + + const tasks = await getModuleWatchTasks({ + garden, + graph, + log, + module, + hotReloadServiceNames: [], + }) + + expect(sortedBaseKeys(tasks)).to.eql(["build.module-a", "deploy.service-a"]) + }) + context("without hot reloading enabled", () => { const expectedBaseKeysByChangedModule = [ { @@ -70,29 +200,153 @@ describe("TaskHelpers", () => { for (const { moduleName, expectedTasks } of expectedBaseKeysByChangedModule) { it(`returns the correct set of tasks for ${moduleName} with dependants`, async () => { + const graph = await depGarden.getConfigGraph(depGarden.log) const module = await graph.getModule(moduleName) + const tasks = await getModuleWatchTasks({ - garden, + garden: depGarden, graph, log, module, - serviceNames: module.serviceNames, hotReloadServiceNames: [], }) expect(sortedBaseKeys(tasks)).to.eql(expectedTasks.sort()) }) } + + it("should omit deploy tasks for disabled services in the module", async () => { + const garden = await makeTestGardenA() + + garden.setModuleConfigs([ + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: false, + name: "module-a", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-a", + dependencies: [], + disabled: true, // <--------------- + }, + ], + }, + testConfigs: [], + type: "test", + }, + ]) + + const graph = await garden.getConfigGraph(garden.log) + const module = await graph.getModule("module-a", true) + + const tasks = await getModuleWatchTasks({ + garden, + graph, + log, + module, + hotReloadServiceNames: [], + }) + + expect(sortedBaseKeys(tasks)).to.eql(["build.module-a"]) + }) + + it("should omit deploy tasks for disabled dependant services", async () => { + const garden = await makeTestGardenA() + + garden.setModuleConfigs([ + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: false, + name: "module-a", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-a", + dependencies: [], + disabled: false, + }, + ], + }, + testConfigs: [], + type: "test", + }, + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [{ name: "module-a", copy: [] }] }, + disabled: false, + name: "module-b", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-b", + dependencies: [], + disabled: true, // <--------------- + }, + ], + }, + testConfigs: [], + type: "test", + }, + ]) + + const graph = await garden.getConfigGraph(garden.log) + const module = await graph.getModule("module-a", true) + + const tasks = await getModuleWatchTasks({ + garden, + graph, + log, + module, + hotReloadServiceNames: [], + }) + + expect(sortedBaseKeys(tasks)).to.eql(["build.module-a", "build.module-b", "deploy.service-a"]) + }) }) context("with hot reloading enabled", () => { const expectedBaseKeysByChangedModule = [ { moduleName: "build-dependency", - expectedTasks: ["build.build-dependency", "deploy.build-dependency"], + expectedTasks: [ + "build.build-dependant", + "build.build-dependency", + "build.good-morning", + "deploy.build-dependant", + "deploy.build-dependency", + "deploy.service-dependant", + "deploy.service-dependant2", + ], }, { moduleName: "good-morning", - expectedTasks: ["deploy.service-dependant", "deploy.service-dependant2", "hot-reload.good-morning"], + expectedTasks: [ + "build.build-dependant", + "deploy.build-dependant", + "deploy.service-dependant", + "deploy.service-dependant2", + "hot-reload.good-morning", + ], }, { moduleName: "good-evening", @@ -110,18 +364,128 @@ describe("TaskHelpers", () => { for (const { moduleName, expectedTasks } of expectedBaseKeysByChangedModule) { it(`returns the correct set of tasks for ${moduleName} with dependants`, async () => { + const graph = await depGarden.getConfigGraph(depGarden.log) const module = await graph.getModule(moduleName) + const tasks = await getModuleWatchTasks({ - garden, + garden: depGarden, graph, log, module, - serviceNames: module.serviceNames, hotReloadServiceNames: ["good-morning"], }) expect(sortedBaseKeys(tasks)).to.eql(expectedTasks.sort()) }) } + + it("should omit hot reload tasks for disabled services in the module", async () => { + const garden = await makeTestGardenA() + + garden.setModuleConfigs([ + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: false, + name: "module-a", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-a", + dependencies: [], + disabled: true, // <--------------- + }, + ], + }, + testConfigs: [], + type: "test", + }, + ]) + + const graph = await garden.getConfigGraph(garden.log) + const module = await graph.getModule("module-a", true) + + const tasks = await getModuleWatchTasks({ + garden, + graph, + log, + module, + hotReloadServiceNames: ["service-a"], + }) + + expect(sortedBaseKeys(tasks)).to.eql([]) + }) + + it("should omit hot reload tasks for disabled dependant services", async () => { + const garden = await makeTestGardenA() + + garden.setModuleConfigs([ + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [] }, + disabled: false, + name: "module-a", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-a", + dependencies: [], + disabled: false, + }, + ], + }, + testConfigs: [], + type: "test", + }, + { + apiVersion: DEFAULT_API_VERSION, + allowPublish: false, + build: { dependencies: [{ name: "module-a", copy: [] }] }, + disabled: false, + name: "module-b", + include: [], + outputs: {}, + path: garden.projectRoot, + serviceConfigs: [], + taskConfigs: [], + spec: { + services: [ + { + name: "service-b", + dependencies: [], + disabled: true, // <--------------- + }, + ], + }, + testConfigs: [], + type: "test", + }, + ]) + + const graph = await garden.getConfigGraph(garden.log) + const module = await graph.getModule("module-a", true) + + const tasks = await getModuleWatchTasks({ + garden, + graph, + log, + module, + hotReloadServiceNames: ["service-a", "service-b"], + }) + + expect(sortedBaseKeys(tasks)).to.eql(["build.module-b", "hot-reload.service-a"]) + }) }) }) })