docker_test.go
⎇
Raw
1package ci
2
3import (
4 "context"
5 "encoding/binary"
6 "net"
7 "net/http"
8 "net/http/httptest"
9 "path/filepath"
10 "testing"
11
12 "hearthforge/internal/config"
13)
14
15// muxFrame builds one Docker multiplexed stream frame.
16func muxFrame(text string) []byte {
17 hdr := make([]byte, 8)
18 hdr[0] = 1
19 binary.BigEndian.PutUint32(hdr[4:], uint32(len(text)))
20 return append(hdr, text...)
21}
22
23// newMockEngine serves a fake engine on a unix socket and returns a Runner
24// pointed at it.
25func newMockEngine(t *testing.T, h http.Handler) *Runner {
26 t.Helper()
27 sock := filepath.Join(t.TempDir(), "engine.sock")
28 ln, err := net.Listen("unix", sock)
29 if err != nil {
30 t.Fatalf("listen: %v", err)
31 }
32 srv := httptest.Server{Listener: ln, Config: &http.Server{Handler: h}}
33 srv.Start()
34 t.Cleanup(srv.Close)
35 return New(&config.Config{CIDockerSocket: sock}, nil)
36}
37
38func TestExecReadsLogAndExitCode(t *testing.T) {
39 r := newMockEngine(t, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
40 switch req.URL.Path {
41 case "/v1.47/containers/abc/exec":
42 w.Write([]byte(`{"Id":"exec1"}`))
43 case "/v1.47/exec/exec1/start":
44 w.Write(muxFrame("hello "))
45 w.Write(muxFrame("world\n"))
46 case "/v1.47/exec/exec1/json":
47 w.Write([]byte(`{"ExitCode":3}`))
48 default:
49 t.Errorf("unexpected request %s", req.URL.Path)
50 }
51 }))
52 res, err := r.exec(context.Background(), "abc", []string{"true"}, "/ci", nil, nil)
53 if err != nil {
54 t.Fatalf("exec: %v", err)
55 }
56 if res.log != "hello world\n" || res.exitCode != 3 {
57 t.Fatalf("got %q exit %d", res.log, res.exitCode)
58 }
59}
60
61func TestSocketMissingIsReported(t *testing.T) {
62 r := New(&config.Config{CIDockerSocket: "/nonexistent/engine.sock"}, nil)
63 _, err := r.exec(context.Background(), "abc", []string{"true"}, "", nil, nil)
64 if err == nil || err.Error() != errNoSocket.Error() {
65 t.Fatalf("got %v", err)
66 }
67}
68