diff --git a/packages/testcontainers/src/container-runtime/image-name.test.ts b/packages/testcontainers/src/container-runtime/image-name.test.ts index ca88122fd..682c9c815 100644 --- a/packages/testcontainers/src/container-runtime/image-name.test.ts +++ b/packages/testcontainers/src/container-runtime/image-name.test.ts @@ -30,6 +30,29 @@ describe("ContainerImage", () => { const imageName = new ImageName("registry", "image", "sha256:1234abcd1234abcd1234abcd1234abcd"); expect(imageName.string).toBe("registry/image@sha256:1234abcd1234abcd1234abcd1234abcd"); }); + + it("should not append the `latest` tag to image IDs", () => { + const imageName = new ImageName( + undefined, + "aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f", + "latest" + ); + expect(imageName.string).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); + }); + + it("should keep other tags (not `latest`) on image IDs", () => { + // Note that the resulting image ID will not be accepted by Docker: + // + // > "invalid repository name [...], cannot specify 64-byte hexadecimal strings" + // + // However, not treating tags other than `latests` specially is probably less surprising. + const imageName = new ImageName( + undefined, + "aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f", + "1" + ); + expect(imageName.string).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f:1"); + }); }); describe("fromString", () => { @@ -96,5 +119,26 @@ describe("ContainerImage", () => { expect(imageName.image).toBe("image"); expect(imageName.tag).toBe("sha256:1234abcd1234abcd1234abcd1234abcd"); }); + + it("should work with image being an image ID", () => { + const imageName = ImageName.fromString("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); + + expect(imageName.registry).toBe(undefined); + expect(imageName.image).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); + expect(imageName.tag).toBe("latest"); + }); + + it("should work with image being an image ID and an explicit tag", () => { + // Note: Such an ID will not be accepted by docker: + // + // > "invalid repository name [...], cannot specify 64-byte hexadecimal strings" + // + // However, parsing it this way is probably least surprising. + const imageName = ImageName.fromString("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f:1"); + + expect(imageName.registry).toBe(undefined); + expect(imageName.image).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); + expect(imageName.tag).toBe("1"); + }); }); }); diff --git a/packages/testcontainers/src/container-runtime/image-name.ts b/packages/testcontainers/src/container-runtime/image-name.ts index c7421c6ed..ea10e1a57 100644 --- a/packages/testcontainers/src/container-runtime/image-name.ts +++ b/packages/testcontainers/src/container-runtime/image-name.ts @@ -1,6 +1,8 @@ export class ImageName { public readonly string: string; + private static readonly hexRE = /^[0-9a-f]{64}$/i; + constructor( public readonly registry: string | undefined, public readonly image: string, @@ -12,6 +14,14 @@ export class ImageName { } else { this.string = `${this.registry}/${this.image}:${this.tag}`; } + } else if (this.tag === "latest" && ImageName.hexRE.test(this.image)) { + // 64 byte hex string. This refers to an image sha256 directly. + // Do not put the tag as the docker does not accept it. + // It will fail with: + // + // invalid repository name (), cannot specify 64-byte hexadecimal strings. + // + this.string = this.image; } else if (this.tag.startsWith("sha256:")) { this.string = `${this.image}@${this.tag}`; } else {