77} from "@cloudflare/containers-shared" ;
88import { http , HttpResponse } from "msw" ;
99import { maybeBuildContainer } from "../../cloudchamber/deploy" ;
10+ import { clearCachedAccount } from "../../cloudchamber/locations" ;
1011import { mockAccountId , mockApiToken } from "../helpers/mock-account-id" ;
1112import { mockConsoleMethods } from "../helpers/mock-console" ;
1213import { mockLegacyScriptData } from "../helpers/mock-legacy-script" ;
@@ -33,7 +34,7 @@ vi.mock("node:child_process");
3334describe ( "maybeBuildContainer" , ( ) => {
3435 it ( "Should return imageUpdate: true if using an image URI" , async ( ) => {
3536 const config = {
36- image : "registry.cloudflare.com/some-account-id/some- image:uri" ,
37+ image : "registry.cloudflare.com/some-image:uri" ,
3738 class_name : "Test" ,
3839 } ;
3940 const result = await maybeBuildContainer (
@@ -121,8 +122,10 @@ describe("wrangler deploy with containers", () => {
121122 )
122123 . mockImplementationOnce ( mockDockerImageInspect ( "my-container" , "Galaxy" ) )
123124 . mockImplementationOnce ( mockDockerLogin ( "mockpassword" ) )
124- . mockImplementationOnce ( mockDockerManifestInspect ( "my-container" , true ) )
125- . mockImplementationOnce ( mockDockerPush ( "my-container" , "Galaxy" ) ) ;
125+ . mockImplementationOnce ( mockDockerManifestInspect ( "some-account-id/my-container" , true ) )
126+ . mockImplementationOnce ( mockDockerTag ( "my-container" , "some-account-id/my-container" , "Galaxy" ) )
127+ . mockImplementationOnce ( mockDockerPush ( "some-account-id/my-container" , "Galaxy" ) )
128+ . mockImplementationOnce ( mockDockerImageDelete ( "some-account-id/my-container" , "Galaxy" ) ) ;
126129
127130 writeWranglerConfig ( {
128131 durable_objects : {
@@ -247,8 +250,10 @@ describe("wrangler deploy with containers", () => {
247250 )
248251 . mockImplementationOnce ( mockDockerImageInspect ( "my-container" , "Galaxy" ) )
249252 . mockImplementationOnce ( mockDockerLogin ( "mockpassword" ) )
250- . mockImplementationOnce ( mockDockerManifestInspect ( "my-container" , true ) )
251- . mockImplementationOnce ( mockDockerPush ( "my-container" , "Galaxy" ) ) ;
253+ . mockImplementationOnce ( mockDockerManifestInspect ( "some-account-id/my-container" , true ) )
254+ . mockImplementationOnce ( mockDockerTag ( "my-container" , "some-account-id/my-container" , "Galaxy" ) )
255+ . mockImplementationOnce ( mockDockerPush ( "some-account-id/my-container" , "Galaxy" ) )
256+ . mockImplementationOnce ( mockDockerImageDelete ( "some-account-id/my-container" , "Galaxy" ) ) ;
252257
253258 mockContainersAccount ( ) ;
254259
@@ -336,8 +341,10 @@ describe("wrangler deploy with containers", () => {
336341 )
337342 . mockImplementationOnce ( mockDockerImageInspect ( "my-container" , "Galaxy" ) )
338343 . mockImplementationOnce ( mockDockerLogin ( "mockpassword" ) )
339- . mockImplementationOnce ( mockDockerManifestInspect ( "my-container" , true ) )
340- . mockImplementationOnce ( mockDockerPush ( "my-container" , "Galaxy" ) ) ;
344+ . mockImplementationOnce ( mockDockerManifestInspect ( "some-account-id/my-container" , true ) )
345+ . mockImplementationOnce ( mockDockerTag ( "my-container" , "some-account-id/my-container" , "Galaxy" ) )
346+ . mockImplementationOnce ( mockDockerPush ( "some-account-id/my-container" , "Galaxy" ) )
347+ . mockImplementationOnce ( mockDockerImageDelete ( "some-account-id/my-container" , "Galaxy" ) ) ;
341348
342349 fs . mkdirSync ( "nested/src" , { recursive : true } ) ;
343350 fs . writeFileSync ( "Dockerfile" , "FROM alpine" ) ;
@@ -437,6 +444,73 @@ describe("wrangler deploy with containers", () => {
437444 } ) ;
438445} ) ;
439446
447+ // This is a separate describe block because we intentionally do not mock any
448+ // API tokens, account ID, or authorization. The purpose of these tests is to
449+ // ensure that --dry-run mode works without requiring any API access.
450+ describe ( "wrangler deploy with containers dry run" , ( ) => {
451+ runInTempDir ( ) ;
452+ const std = mockConsoleMethods ( ) ;
453+
454+ beforeEach ( ( ) => {
455+ clearCachedAccount ( ) ;
456+ } ) ;
457+
458+ afterEach ( ( ) => {
459+ vi . unstubAllEnvs ( ) ;
460+ } ) ;
461+
462+ it ( "builds the image without pushing" , async ( ) => {
463+ vi . mocked ( spawn )
464+ . mockImplementationOnce ( mockDockerInfo ( ) )
465+ . mockImplementationOnce (
466+ mockDockerBuild ( "my-container" , "worker" , "FROM scratch" , process . cwd ( ) )
467+ )
468+ . mockImplementationOnce ( mockDockerImageInspect ( "my-container" , "worker" ) )
469+ . mockImplementationOnce ( mockDockerLogin ( "mockpassword" ) )
470+ . mockImplementationOnce ( mockDockerManifestInspect ( "my-container" , true ) )
471+ . mockImplementationOnce ( mockDockerPush ( "my-container" , "worker" ) ) ;
472+
473+ vi . stubEnv ( "WRANGLER_DOCKER_BIN" , "/usr/bin/docker" ) ;
474+
475+ fs . writeFileSync ( "Dockerfile" , "FROM scratch" ) ;
476+ fs . writeFileSync (
477+ "index.js" ,
478+ `export class ExampleDurableObject {}; export default{};`
479+ ) ;
480+
481+ writeWranglerConfig ( {
482+ durable_objects : {
483+ bindings : [
484+ {
485+ name : "EXAMPLE_DO_BINDING" ,
486+ class_name : "ExampleDurableObject" ,
487+ } ,
488+ ] ,
489+ } ,
490+ containers : [
491+ {
492+ image : "./Dockerfile" ,
493+ name : "my-container" ,
494+ instances : 10 ,
495+ class_name : "ExampleDurableObject" ,
496+ } ,
497+ ] ,
498+ migrations : [ { tag : "v1" , new_sqlite_classes : [ "ExampleDurableObject" ] } ] ,
499+ } ) ;
500+
501+ await runWrangler ( "deploy --dry-run index.js" ) ;
502+ expect ( std . out ) . toMatchInlineSnapshot ( `
503+ "Total Upload: xx KiB / gzip: xx KiB
504+ Building image my-container:worker
505+ Your Worker has access to the following bindings:
506+ Binding Resource
507+ env.EXAMPLE_DO_BINDING (ExampleDurableObject) Durable Object
508+
509+ --dry-run: exiting now."
510+ ` ) ;
511+ } ) ;
512+ } ) ;
513+
440514function mockGetVersion ( versionId : string ) {
441515 msw . use (
442516 http . get (
@@ -549,7 +623,7 @@ function mockDockerBuild(
549623 expect ( args ) . toEqual ( [
550624 "build" ,
551625 "-t" ,
552- `${ getCloudflareContainerRegistry ( ) } /some-account-id/ ${ containerName } :${ tag } ` ,
626+ `${ getCloudflareContainerRegistry ( ) } /${ containerName } :${ tag } ` ,
553627 "--platform" ,
554628 "linux/amd64" ,
555629 "--provenance=false" ,
@@ -590,7 +664,7 @@ function mockDockerImageInspect(containerName: string, tag: string) {
590664 expect ( args ) . toEqual ( [
591665 "image" ,
592666 "inspect" ,
593- `${ getCloudflareContainerRegistry ( ) } /some-account-id/ ${ containerName } :${ tag } ` ,
667+ `${ getCloudflareContainerRegistry ( ) } /${ containerName } :${ tag } ` ,
594668 "--format" ,
595669 "{{ .Size }} {{ len .RootFS.Layers }} {{json .RepoDigests}}" ,
596670 ] ) ;
@@ -612,14 +686,26 @@ function mockDockerImageInspect(containerName: string, tag: string) {
612686 setImmediate ( ( ) => {
613687 stdout . emit (
614688 "data" ,
615- `123456 4 ["${ getCloudflareContainerRegistry ( ) } /some-account-id/ ${ containerName } @sha256:three"]`
689+ `123456 4 ["${ getCloudflareContainerRegistry ( ) } /${ containerName } @sha256:three"]`
616690 ) ;
617691 } ) ;
618692
619693 return child as unknown as ChildProcess ;
620694 } ;
621695}
622696
697+ function mockDockerImageDelete ( containerName : string , tag : string ) {
698+ return ( cmd : string , args : readonly string [ ] ) => {
699+ expect ( cmd ) . toBe ( "/usr/bin/docker" ) ;
700+ expect ( args ) . toEqual ( [
701+ "image" ,
702+ "rm" ,
703+ `${ getCloudflareContainerRegistry ( ) } /${ containerName } :${ tag } ` ,
704+ ] ) ;
705+ return defaultChildProcess ( ) ;
706+ } ;
707+ }
708+
623709function mockDockerLogin ( expectedPassword : string ) {
624710 return ( cmd : string , _args : readonly string [ ] ) => {
625711 expect ( cmd ) . toBe ( "/usr/bin/docker" ) ;
@@ -653,7 +739,7 @@ function mockDockerManifestInspect(containerName: string, shouldFail = true) {
653739 expect ( args ) . toEqual ( [
654740 "manifest" ,
655741 "inspect" ,
656- `${ getCloudflareContainerRegistry ( ) } /some-account-id/ ${ containerName } @three` ,
742+ `${ getCloudflareContainerRegistry ( ) } /${ containerName } @three` ,
657743 ] ) ;
658744 const readable = new Writable ( {
659745 write ( ) { } ,
@@ -679,7 +765,19 @@ function mockDockerPush(containerName: string, tag: string) {
679765 expect ( cmd ) . toBe ( "/usr/bin/docker" ) ;
680766 expect ( args ) . toEqual ( [
681767 "push" ,
682- `${ getCloudflareContainerRegistry ( ) } /some-account-id/${ containerName } :${ tag } ` ,
768+ `${ getCloudflareContainerRegistry ( ) } /${ containerName } :${ tag } ` ,
769+ ] ) ;
770+ return defaultChildProcess ( ) ;
771+ } ;
772+ }
773+
774+ function mockDockerTag ( from : string , to : string , tag : string ) {
775+ return ( cmd : string , args : readonly string [ ] ) => {
776+ expect ( cmd ) . toBe ( "/usr/bin/docker" ) ;
777+ expect ( args ) . toEqual ( [
778+ "tag" ,
779+ `${ getCloudflareContainerRegistry ( ) } /${ from } :${ tag } ` ,
780+ `${ getCloudflareContainerRegistry ( ) } /${ to } :${ tag } ` ,
683781 ] ) ;
684782 return defaultChildProcess ( ) ;
685783 } ;
0 commit comments