1// SPDX-License-Identifier: MIT
2
3package executor
4
5import (
6 "strings"
7 "testing"
8)
9
10func TestInitAndStopServiceOnly(t *testing.T) {
11 f := newFakeCtr(t)
12 f.set(t, "health", "starting\nhealthy\n")
13 f.set(t, "ports", "6379/tcp -> 0.0.0.0:16379\n6379/tcp -> [::]:16379\n")
14 rec, res := ctrRun(t, f, " services:\n redis:\n image: redis:7\n ports:\n - 16379:6379\n options: >-\n --health-cmd \"redis-cli ping\"\n --health-retries 10\n",
15 " - name: ctx\n run: echo \"p=${{ job.services.redis.ports['6379'] }} n=${{ job.services.redis.network != '' }}\"\n", nil)
16 if res.Conclusion != ConclusionSuccess {
17 t.Fatalf("%+v %v", res, rec.names())
18 }
19 if got := strings.Join(rec.names(), "|"); got != "Set up job|Initialize containers|ctx|Stop containers|Complete job" {
20 t.Fatalf("steps %s", got)
21 }
22 init := logOf(rec.step(t, "Initialize containers"))
23 for _, w := range []string{
24 "##[group]Checking docker version\n##[command]", "Docker daemon API version: '1.48'", "Docker client API version: '1.48'",
25 "##[group]Clean up resources from previous jobs", "##[group]Create local container network",
26 "##[group]Starting redis service container", " network create --label ",
27 "--network-alias redis -p 16379:6379 --health-cmd \"redis-cli ping\" --health-retries 10 -e GITHUB_ACTIONS=true -e CI=true redis:7",
28 "##[group]Waiting for all services to be ready", "redis service is starting, waiting 0 seconds before checking again.", "redis service is healthy.",
29 } {
30 if !strings.Contains(init, w) {
31 t.Errorf("init log lacks %q:\n%s", w, init)
32 }
33 }
34 if strings.Contains(init, "--workdir") || strings.Contains(init, "--entrypoint") {
35 t.Errorf("a service has neither workdir nor entrypoint:\n%s", init)
36 }
37 stop := logOf(rec.step(t, "Stop containers"))
38 if !strings.Contains(stop, "Print service container logs: ") || !strings.Contains(stop, "Stop and remove container: ") ||
39 !strings.Contains(stop, "Remove container network: github_network_") || !strings.Contains(stop, "network rm github_network_") {
40 t.Errorf("stop:\n%s", stop)
41 }
42 want := "version version ps network prune network create pull create start ps port inspect inspect logs rm network rm"
43 if got := strings.Join(f.verbs(t), " "); got != want {
44 t.Errorf("docker calls:\n%s\nwant\n%s", got, want)
45 }
46 run := logOf(rec.step(t, "ctx"))
47 if !strings.Contains(run, "p=16379 n=true") {
48 t.Errorf("job.services not in the context:\n%s", run)
49 }
50}
51
52// GO-004: values of container variables are in the environment of the docker
53// process; the arguments carry "-e NAME". The echoed line is the runner's
54// (recorded: -e "NAME=value"), masked like any log.
55func TestContainerEnvValuesStayOffTheArguments(t *testing.T) {
56 f := newFakeCtr(t)
57 rec, res := ctrRun(t, f, " container:\n image: ubuntu:24.04\n env:\n CVAR: s3cret-value\n EMPTYV: \"\"\n DOCKER_HOST: tcp://x:1\n",
58 " - name: p\n run: echo\n - name: q\n env:\n STEPVAR: s3cret-step\n run: echo\n", func(c *Config) { f.set(t, "add_path", c.Layout.WorkRoot) })
59 if res.Conclusion != ConclusionSuccess {
60 t.Fatalf("%+v", res)
61 }
62 var create []string
63 for _, c := range f.calls(t) {
64 if c[0] == "create" {
65 create = c
66 }
67 }
68 joined := strings.Join(create, "\n")
69 for _, w := range []string{"\n-e\nCVAR\n", "\n-e\nEMPTYV\n", "\n-e\nDOCKER_HOST=tcp://x:1\n", "ENV:CVAR=s3cret-value:EMPTYV=:"} {
70 if !strings.Contains(joined, w) {
71 t.Errorf("create lacks %q:\n%s", w, joined)
72 }
73 }
74 if strings.Contains(joined, "CVAR=s3cret") && !strings.Contains(joined, "ENV:CVAR=s3cret") {
75 t.Errorf("value on the arguments:\n%s", joined)
76 }
77 for _, a := range create {
78 if strings.HasPrefix(a, "CVAR=") || strings.HasPrefix(a, "EMPTYV=") {
79 t.Errorf("argument %q carries a value", a)
80 }
81 }
82 // What the runner echoes (recorded in ctr-options).
83 init := logOf(rec.step(t, "Initialize containers"))
84 if !strings.Contains(init, ` -e "CVAR=s3cret-value" -e "EMPTYV" `) {
85 t.Errorf("echo:\n%s", init)
86 }
87 // exec: no value of a step variable and no PATH on the arguments.
88 for _, c := range f.execCalls(t) {
89 for _, a := range c {
90 if strings.HasPrefix(a, "ENV:") {
91 continue // what the fake saw in its environment
92 }
93 if strings.Contains(a, "s3cret-step") || strings.HasPrefix(a, "PATH=") || strings.Contains(a, "/added") {
94 t.Errorf("exec argument %q", a)
95 }
96 }
97 }
98 ex := f.execCalls(t)
99 last := strings.Join(ex[len(ex)-1], "\n")
100 if !strings.Contains(last, "\n-e\nPATH\n") || !strings.Contains(last, "ENV:") || !strings.Contains(last, "PATH=/added:") {
101 t.Errorf("second exec:\n%s", last)
102 }
103}