~bigbes/sr-ht-dolt

ref: 4d178626e1f2d04330127c394e0684a78a2d7102 sr-ht-dolt/cmd/dolt-git-hook/main_test.go -rw-r--r-- 3.9 KiB
4d178626 — Eugene Blikh log: bridge dolt's remotesrv logger into slog 9 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"sourcecraft.dev/bigbes/sr-ht-core/crypto"

	"sourcecraft.dev/bigbes/sr-ht-ecore/ecoretest"
)

// initTestCrypto installs sr-ht-ecore's fixed test keyset into the shared
// crypto globals, so createCompanion's crypto.Encrypt and the test server's
// DecryptWithExpiration share one.
func initTestCrypto(t *testing.T) {
	t.Helper()
	ecoretest.InitCrypto()
}

func samplePush() pushContext {
	var pc pushContext
	pc.Repo.Name = "widgets"
	pc.Repo.OwnerName = "alice"
	pc.Repo.Visibility = "private"
	return pc
}

// decodeInternalAuth verifies an incoming "Internal <token>" header the same way
// dolt.sr.ht's internalAuthGuard does and returns the decoded claims.
func decodeInternalAuth(t *testing.T, r *http.Request) internalAuth {
	t.Helper()
	parts := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
	if len(parts) != 2 || !strings.EqualFold(parts[0], "internal") {
		t.Fatalf("missing/invalid Internal auth header: %q", r.Header.Get("Authorization"))
	}
	payload := crypto.DecryptWithExpiration([]byte(parts[1]), 30*time.Second)
	if payload == nil {
		t.Fatalf("auth token did not decrypt (wrong key or expired)")
	}
	var ia internalAuth
	if err := json.Unmarshal(payload, &ia); err != nil {
		t.Fatalf("auth payload not JSON: %v", err)
	}
	return ia
}

func TestCreateCompanionSignsAndAnnouncesOnCreate(t *testing.T) {
	initTestCrypto(t)

	var gotBody map[string]string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/internal/repos" || r.Method != http.MethodPost {
			t.Errorf("unexpected request %s %s", r.Method, r.URL.Path)
		}
		ia := decodeInternalAuth(t, r)
		if ia.ClientID != "git.sr.ht" || ia.NodeID != "dolt-git-hook" {
			t.Errorf("unexpected auth claims: %+v", ia)
		}
		raw, _ := io.ReadAll(r.Body)
		_ = json.Unmarshal(raw, &gotBody)
		w.WriteHeader(http.StatusCreated)
		_, _ = w.Write([]byte(`{"url":"https://dolt.example/~alice/widgets","created":true}`))
	}))
	defer srv.Close()

	var out bytes.Buffer
	createCompanion(&out, srv.URL, samplePush())

	if gotBody["owner"] != "alice" || gotBody["name"] != "widgets" {
		t.Fatalf("unexpected request body: %+v", gotBody)
	}
	if gotBody["visibility"] != "PRIVATE" {
		t.Fatalf("visibility not normalized to PRIVATE: %q", gotBody["visibility"])
	}
	if !strings.Contains(out.String(), "NOTICE") ||
		!strings.Contains(out.String(), "dolt clone https://dolt.example/~alice/widgets") {
		t.Fatalf("expected clone notice, got: %q", out.String())
	}
}

func TestCreateCompanionQuietWhenExists(t *testing.T) {
	initTestCrypto(t)
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		decodeInternalAuth(t, r) // still must be authenticated
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(`{"url":"https://dolt.example/~alice/widgets","created":false}`))
	}))
	defer srv.Close()

	var out bytes.Buffer
	createCompanion(&out, srv.URL, samplePush())
	if out.Len() != 0 {
		t.Fatalf("expected no output for existing companion, got: %q", out.String())
	}
}

func TestCreateCompanionWarnsOnError(t *testing.T) {
	initTestCrypto(t)
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
		_, _ = w.Write([]byte("boom"))
	}))
	defer srv.Close()

	var out bytes.Buffer
	createCompanion(&out, srv.URL, samplePush())
	if !strings.Contains(out.String(), "provisioning failed (500)") {
		t.Fatalf("expected failure warning, got: %q", out.String())
	}
}

func TestNormalizeVisibility(t *testing.T) {
	cases := map[string]string{
		"private": "PRIVATE", "PUBLIC": "PUBLIC", "Unlisted": "UNLISTED",
		"": "", "bogus": "",
	}
	for in, want := range cases {
		if got := normalizeVisibility(in); got != want {
			t.Errorf("normalizeVisibility(%q) = %q, want %q", in, got, want)
		}
	}
}