This repository has no description
1package microvm
2
3import (
4 "io"
5 "log/slog"
6 "net/http"
7 "net/http/httptest"
8 "net/url"
9 "strings"
10 "testing"
11)
12
13func TestUploadProxyRewritesHostAndAuth(t *testing.T) {
14 var upstreamHost string
15 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
16 if req.Host != upstreamHost {
17 t.Errorf("host: got %q, want %q", req.Host, upstreamHost)
18 }
19 if req.URL.Path != "/sub/abc.narinfo" {
20 t.Errorf("path: got %q, want /sub/abc.narinfo", req.URL.Path)
21 }
22 if user, pass, ok := req.BasicAuth(); !ok || user != "dawn" || pass != "woof" {
23 t.Errorf("basic auth: got %q/%q/%v, want dawn/hunter2/true", user, pass, ok)
24 }
25 _, _ = io.WriteString(w, "ok")
26 }))
27 defer upstream.Close()
28 upstreamHost = strings.TrimPrefix(upstream.URL, "http://")
29
30 target, err := url.Parse("http://dawn:woof@" + upstreamHost + "/sub/")
31 if err != nil {
32 t.Fatal(err)
33 }
34
35 req := httptest.NewRequest(http.MethodPut, "http://127.0.0.1:10501/abc.narinfo", strings.NewReader("narinfo"))
36 req.Host = "127.0.0.1:10501"
37 rec := httptest.NewRecorder()
38 uploadProxyHandler(target, nil, slog.Default()).ServeHTTP(rec, req)
39
40 if rec.Code != http.StatusOK {
41 t.Fatalf("status: got %d, want 200; body=%q", rec.Code, rec.Body.String())
42 }
43}
44
45func mustParseURL(t *testing.T, raw string) *url.URL {
46 t.Helper()
47 u, err := url.Parse(raw)
48 if err != nil {
49 t.Fatalf("parse %q: %v", raw, err)
50 }
51 return u
52}
53
54func TestUploadProxySkipsNarinfoAvailableUpstream(t *testing.T) {
55 var uploadHits int
56 target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
57 uploadHits++
58 w.WriteHeader(http.StatusNotFound)
59 }))
60 defer target.Close()
61
62 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
63 if req.URL.Path != "/abc.narinfo" {
64 t.Errorf("upstream path: got %q, want /abc.narinfo", req.URL.Path)
65 }
66 _, _ = io.WriteString(w, "StorePath: /nix/store/abc\n")
67 }))
68 defer upstream.Close()
69
70 handler := uploadProxyHandler(
71 mustParseURL(t, target.URL),
72 []CacheUpstream{{url: mustParseURL(t, upstream.URL)}},
73 slog.Default(),
74 )
75
76 req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:10501/abc.narinfo", nil)
77 rec := httptest.NewRecorder()
78 handler.ServeHTTP(rec, req)
79
80 if rec.Code != http.StatusOK {
81 t.Fatalf("status: got %d, want 200 (so nix treats the path as present and skips upload)", rec.Code)
82 }
83 if !strings.Contains(rec.Body.String(), "StorePath: /nix/store/abc") {
84 t.Fatalf("body: got %q, want the upstream narinfo body", rec.Body.String())
85 }
86}
87
88func TestUploadProxyUploadsNarinfoNobodyHas(t *testing.T) {
89 target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
90 w.WriteHeader(http.StatusNotFound)
91 }))
92 defer target.Close()
93 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
94 w.WriteHeader(http.StatusNotFound)
95 }))
96 defer upstream.Close()
97
98 handler := uploadProxyHandler(
99 mustParseURL(t, target.URL),
100 []CacheUpstream{{url: mustParseURL(t, upstream.URL)}},
101 slog.Default(),
102 )
103
104 req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:10501/abc.narinfo", nil)
105 rec := httptest.NewRecorder()
106 handler.ServeHTTP(rec, req)
107
108 if rec.Code != http.StatusNotFound {
109 t.Fatalf("status: got %d, want 404 (so nix uploads the path)", rec.Code)
110 }
111}
112
113func TestUploadProxySkipsNarinfoAlreadyOnTarget(t *testing.T) {
114 target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
115 _, _ = io.WriteString(w, "StorePath: /nix/store/abc\n")
116 }))
117 defer target.Close()
118
119 handler := uploadProxyHandler(mustParseURL(t, target.URL), nil, slog.Default())
120
121 req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:10501/abc.narinfo", nil)
122 rec := httptest.NewRecorder()
123 handler.ServeHTTP(rec, req)
124
125 if rec.Code != http.StatusOK {
126 t.Fatalf("status: got %d, want 200", rec.Code)
127 }
128}