Explorar o código

add e2e tests

Signed-off-by: Suleiman Dibirov <[email protected]>
(cherry picked from commit 0c854a6ab7331294533f8277b62ae97f8c53fb47)
Signed-off-by: Guillaume Lours <[email protected]>
Suleiman Dibirov hai 2 meses
pai
achega
c6bec2e712

+ 87 - 0
pkg/e2e/compose_run_build_once_test.go

@@ -0,0 +1,87 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package e2e
+
+import (
+	"strings"
+	"testing"
+
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+)
+
+// TestRunBuildOnce tests that services with pull_policy: build are only built once
+// when using 'docker compose run', even when they are dependencies.
+// This addresses a bug where dependencies were built twice: once in startDependencies
+// and once in ensureImagesExists.
+func TestRunBuildOnce(t *testing.T) {
+	c := NewParallelCLI(t)
+
+	t.Run("dependency with pull_policy build is built only once", func(t *testing.T) {
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once.yaml", "down", "--rmi", "local", "--remove-orphans")
+		res.Assert(t, icmd.Success)
+
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once.yaml", "run", "--rm", "curl")
+		res.Assert(t, icmd.Success)
+
+		// Count how many times nginx was built by looking for its unique RUN command output
+		nginxBuilds := strings.Count(res.Combined(), "Building nginx at")
+
+		// nginx should build exactly once, not twice
+		assert.Equal(t, nginxBuilds, 1, "nginx dependency should build once, but built %d times", nginxBuilds)
+		assert.Assert(t, strings.Contains(res.Combined(), "curl service"))
+
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once.yaml", "down", "--remove-orphans")
+	})
+
+	t.Run("nested dependencies build only once each", func(t *testing.T) {
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once-nested.yaml", "down", "--rmi", "local", "--remove-orphans")
+		res.Assert(t, icmd.Success)
+
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once-nested.yaml", "run", "--rm", "app")
+		res.Assert(t, icmd.Success)
+
+		output := res.Combined()
+
+		// Each service should build exactly once
+		dbBuilds := strings.Count(output, "DB built at")
+		apiBuilds := strings.Count(output, "API built at")
+		appBuilds := strings.Count(output, "App built at")
+
+		assert.Equal(t, dbBuilds, 1, "db should build once, built %d times", dbBuilds)
+		assert.Equal(t, apiBuilds, 1, "api should build once, built %d times", apiBuilds)
+		assert.Equal(t, appBuilds, 1, "app should build once, built %d times", appBuilds)
+		assert.Assert(t, strings.Contains(output, "App running"))
+
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once-nested.yaml", "down", "--remove-orphans")
+	})
+
+	t.Run("service with no dependencies builds once", func(t *testing.T) {
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once-no-deps.yaml", "down", "--rmi", "local", "--remove-orphans")
+		res.Assert(t, icmd.Success)
+
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once-no-deps.yaml", "run", "--rm", "simple")
+		res.Assert(t, icmd.Success)
+
+		// Should build exactly once
+		simpleBuilds := strings.Count(res.Combined(), "Simple service built at")
+		assert.Equal(t, simpleBuilds, 1, "simple should build once, built %d times", simpleBuilds)
+		assert.Assert(t, strings.Contains(res.Combined(), "Simple service"))
+
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/build-once-no-deps.yaml", "down", "--remove-orphans")
+	})
+}

+ 32 - 0
pkg/e2e/fixtures/run-test/build-once-nested.yaml

@@ -0,0 +1,32 @@
+services:
+  # Database service with build
+  db:
+    pull_policy: build
+    build:
+      dockerfile_inline: |
+        FROM alpine
+        RUN echo "DB built at $(date)" > /db-build.txt
+        CMD sleep 3600
+
+  # API service that depends on db
+  api:
+    pull_policy: build
+    build:
+      dockerfile_inline: |
+        FROM alpine
+        RUN echo "API built at $(date)" > /api-build.txt
+        CMD sleep 3600
+    depends_on:
+      - db
+
+  # App service that depends on api (which depends on db)
+  app:
+    pull_policy: build
+    build:
+      dockerfile_inline: |
+        FROM alpine
+        RUN echo "App built at $(date)" > /app-build.txt
+        CMD echo "App running"
+    depends_on:
+      - api
+

+ 10 - 0
pkg/e2e/fixtures/run-test/build-once-no-deps.yaml

@@ -0,0 +1,10 @@
+services:
+  # Simple service with no dependencies
+  simple:
+    pull_policy: build
+    build:
+      dockerfile_inline: |
+        FROM alpine
+        RUN echo "Simple service built at $(date)" > /build.txt
+        CMD echo "Simple service"
+

+ 18 - 0
pkg/e2e/fixtures/run-test/build-once.yaml

@@ -0,0 +1,18 @@
+services:
+  # Service with pull_policy: build to ensure it always rebuilds
+  # This is the key to testing the bug - without the fix, this would build twice
+  nginx:
+    pull_policy: build
+    build:
+      dockerfile_inline: |
+        FROM alpine
+        RUN echo "Building nginx at $(date)" > /build-time.txt
+        CMD sleep 3600
+
+  # Service that depends on nginx
+  curl:
+    image: alpine
+    depends_on:
+      - nginx
+    command: echo "curl service"
+