Skip to main content

TestModuleContainer

The harness for testing a module.

It runs your module through the real Octo lifecycle against an in-memory state provider, and hands back everything a test needs to assert on: the model and resource transactions, the generated HCL, and the resource responses. Create it inside a TestContainer so each test file is isolated.

@example
const container = await TestContainer.create({ mocks: [] }, { factoryTimeoutInMs: 500 });
const testModuleContainer = new TestModuleContainer(container);
await testModuleContainer.initialize();

const generator = testModuleContainer.runModules([
{ inputs: { name: 'test-app' }, moduleId: 'app', type: SimpleAppModule },
]);
const { modelTransaction } = (await generator.next()).value;
@group

Modules

Index

Constructors

constructor

Methods

createResources

  • createResources(moduleId: string, args: UnknownResource[], options?: { moduleId?: string; save?: boolean }): Promise<Promise<{}>>
  • To seed resources that already exist, as though the given module had produced them. Use it to stand up the prerequisites a module under test expects to find.


    Parameters

    • moduleId: string
    • args: UnknownResource[]
    • optionaloptions: { moduleId?: string; save?: boolean }
      • optionalmoduleId: string
      • optionalsave: boolean

    Returns Promise<Promise<{}>>

createTestAnchor

  • createTestAnchor<S>(anchorId: string, properties: S[properties], parent: S[parentInstance]): UnknownAnchor
  • To build a throwaway anchor for a test double, without declaring an anchor class.


    Type parameters

    Parameters

    • anchorId: string
    • properties: S[properties]
    • parent: S[parentInstance]

    Returns UnknownAnchor

createTestModels

  • To seed models, as though the given module had produced them. Each model gets a no-op action, so the models appear in a transaction without doing any work.


    Parameters

    • moduleId: string
    • args: { account?: (string | Account)[]; app?: (string | App)[]; deployment?: (string | Deployment)[]; environment?: (string | Environment)[]; execution?: (string | Execution)[]; filesystem?: (string | Filesystem)[]; image?: (string | Image)[]; pipeline?: (string | Pipeline)[]; region?: (string | Region)[]; server?: (string | Server)[]; service?: (Service | [string, Record<string, unknown>?, UnknownModel[]?])[]; subnet?: (string | Subnet)[] }
      • optionalaccount: (string | Account)[]
      • optionalapp: (string | App)[]
      • optionaldeployment: (string | Deployment)[]
      • optionalenvironment: (string | Environment)[]
      • optionalexecution: (string | Execution)[]
      • optionalfilesystem: (string | Filesystem)[]
      • optionalimage: (string | Image)[]
      • optionalpipeline: (string | Pipeline)[]
      • optionalregion: (string | Region)[]
      • optionalserver: (string | Server)[]
      • optionalservice: (Service | [string, Record<string, unknown>?, UnknownModel[]?])[]
      • optionalsubnet: (string | Subnet)[]

    Returns Promise<{ account: Account[]; app: App[]; deployment: Deployment[]; environment: Environment[]; execution: Execution[]; filesystem: Filesystem[]; image: Image[]; pipeline: Pipeline[]; region: Region[]; server: Server[]; service: Service[]; subnet: Subnet[] }>

createTestOverlays

  • createTestOverlays(moduleId: string, args: { anchors: UnknownAnchor[]; context: string; overlayActions?: IUnknownModelAction[]; properties?: {} }[]): Promise<Promise<{}>>
  • To seed overlays, as though the given module had produced them. An overlay given no actions gets a no-op one.


    Parameters

    • moduleId: string
    • args: { anchors: UnknownAnchor[]; context: string; overlayActions?: IUnknownModelAction[]; properties?: {} }[]

    Returns Promise<Promise<{}>>

createTestResources

  • createTestResources<S>(moduleId: string, args: { [ K in string | number | symbol ]: Partial<Pick<S[K], properties | response | tags>> & { parents?: UnknownResource[] | string[]; resourceActions?: IUnknownResourceAction[]; resourceContext: string; schema?: Constructable<S[K]>; terraform?: boolean } }, options?: { moduleId?: string; save?: boolean }): Promise<Promise<{}>>
  • To seed resources, as though the given module had produced them. A resource given no actions gets a no-op one, unless terraform owns it.


    Type parameters

    Parameters

    • moduleId: string
    • args: { [ K in string | number | symbol ]: Partial<Pick<S[K], properties | response | tags>> & { parents?: UnknownResource[] | string[]; resourceActions?: IUnknownResourceAction[]; resourceContext: string; schema?: Constructable<S[K]>; terraform?: boolean } }
    • optionaloptions: { moduleId?: string; save?: boolean }
      • optionalmoduleId: string
      • optionalsave: boolean

    Returns Promise<Promise<{}>>

digestDiffs

  • Reduces a transaction's diffs, model or resource, to a stable digest you can snapshot: + <context> (create), - <context> (delete), ^ <context> (replace), * <context> (update).

    A diff is classified exactly as Octo.validate aggregates it, so a digest and a validate report of the same transaction never disagree. Only an add/delete of a resourceId creates or destroys a resource; an add/delete on any other field changes a resource that stays - re-pointing a parent, most commonly - and reads as an update.

    The digest has one entry per diff, not per resource, so a resource may appear more than once.


    Parameters

    Returns string[]

initialize

  • initialize(stateProvider?: IStateProvider, initializeInContainer?: { options?: { args?: unknown[]; metadata?: {} }; type: string | Constructable<unknown> }[], excludeInContainer?: { type: string | Constructable<unknown> }[]): Promise<void>
  • Boots Octo for the test. Call it once, before running any module.


    Parameters

    • optionalstateProvider: IStateProvider

      Defaults to an in-memory TestStateProvider. Only the local and test providers are accepted - a test must never write to shared state.

    • optionalinitializeInContainer: { options?: { args?: unknown[]; metadata?: {} }; type: string | Constructable<unknown> }[]

      Factories to resolve at boot. See Octo.initialize.

    • optionalexcludeInContainer: { type: string | Constructable<unknown> }[]

      Factories to unregister at boot. See Octo.initialize.

    Returns Promise<void>

isResourceStateEqual

  • isResourceStateEqual(): Promise<boolean>
  • Whether the committed resource state matches the previous one, i.e. whether the last run settled without leaving a pending change.


    Returns Promise<boolean>

mapTransactionActions

  • mapTransactionActions(transaction: DiffMetadata[][]): string[][]
  • Reduces a transaction to the names of the actions it ran, in order. Snapshot it to assert that a change triggered the actions you expected.


    Parameters

    Returns string[][]

registerHooks

  • registerHooks(...args: [{ postCommitHooks?: PostHookSignature[]; postModelActionHooks?: PostHookSignature[]; postResourceActionHooks?: PostHookSignature[]; preCommitHooks?: PreHookSignature[]; preModelActionHooks?: PreHookSignature[]; preResourceActionHooks?: PreHookSignature[] }?]): void

  • Parameters

    • rest...args: [{ postCommitHooks?: PostHookSignature[]; postModelActionHooks?: PostHookSignature[]; postResourceActionHooks?: PostHookSignature[]; preCommitHooks?: PreHookSignature[]; preModelActionHooks?: PreHookSignature[]; preResourceActionHooks?: PreHookSignature[] }?]

    Returns void

registerTags

  • registerTags(...args: [args: { scope: { moduleId?: string; resourceContext?: string }; tags: {} }[]]): void

  • Parameters

    • rest...args: [args: { scope: { moduleId?: string; resourceContext?: string }; tags: {} }[]]

    Returns void

registerTerraformConfig

  • registerTerraformConfig(...args: [{ minTerraformVersion?: string; providers?: Record<string, { minVersion?: string; source: string }> }?]): void
  • See Octo.registerTerraformConfig. Required before a run that generates terraform.


    Parameters

    • rest...args: [{ minTerraformVersion?: string; providers?: Record<string, { minVersion?: string; source: string }> }?]

    Returns void

registerTerraformProvider

  • registerTerraformProvider(...args: [providerType: string, accountId: string, regionId: string, spec: Record<string, unknown>, { accountName?: string; setRegionAttribute?: boolean }?]): void
  • See Octo.registerTerraformProvider. Required before a run that generates terraform.


    Parameters

    • rest...args: [providerType: string, accountId: string, regionId: string, spec: Record<string, unknown>, { accountName?: string; setRegionAttribute?: boolean }?]

    Returns void

reset

  • reset(): Promise<void>
  • Tears down the composed graph - models, overlays, inputs, and modules - so the next TestModuleContainer.runModules composes fresh. Call it in your test teardown.


    Returns Promise<void>

runModules

  • runModules<M>(modules: TestModule<M> | TestModule<M>[], options?: { filterByModuleIds?: string[]; outputDir?: string; terraformTarget?: apply | plan | skip }): AsyncGenerator<{ hclDiff: string; hclRender: string; modelTransaction: DiffMetadata[][]; resourceDiffs: DiffMetadata[][]; resourceTransaction: DiffMetadata[][]; responses: {}; validateReport: ValidateReport | undefined; warnings: { message: string; moduleId?: string }[] }>
  • Runs the given modules through Octo's lifecycle, and yields the result once.

    How far the lifecycle goes is set by terraformTarget, and this is the choice that decides what the test costs:

    • 'skip' - composes and commits in memory. No terraform, no cloud. This is a unit test.
    • 'plan' - generates the module folders and runs terraform plan against them.
    • 'apply' - generates, plans, applies, and commits. This creates real infrastructure.

    Type parameters

    • M: UnknownModule

    Parameters

    • modules: TestModule<M> | TestModule<M>[]

      The modules to load, in order.

    • options: { filterByModuleIds?: string[]; outputDir?: string; terraformTarget?: apply | plan | skip } = {}

      filterByModuleIds narrows the run to those modules; outputDir is where the terraform folders are written, defaulting to a temporary directory.

      • optionalfilterByModuleIds: string[] = []
      • optionaloutputDir: string
      • optionalterraformTarget: apply | plan | skip = 'skip'

    Returns AsyncGenerator<{ hclDiff: string; hclRender: string; modelTransaction: DiffMetadata[][]; resourceDiffs: DiffMetadata[][]; resourceTransaction: DiffMetadata[][]; responses: {}; validateReport: ValidateReport | undefined; warnings: { message: string; moduleId?: string }[] }>