~bigbes/sr-ht-dolt

ref: 8ba716c343fd6c99a38fe208cb07268c08ac2f18 sr-ht-dolt/cmd/dolt-git-hook/main_test.go -rw-r--r-- 4.3 KiB
8ba716c3 — Eugene Blikh instconf: take the origin and required-key helpers from ecore 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
129
130
131
132
133
package main

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

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

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

	"sourcecraft.dev/bigbes/sr-ht-dolt/core"
)

// TestMain installs the two pieces of core-go process state the provisioning
// call reads and neither creates: the fixed test keyset, so the token this hook
// seals is one internalauth can open, and a loaded config, whose only job here
// is to fill the internal network list — without it that list is empty, every
// address is external, and the httptest server's loopback caller is refused
// before its token is ever looked at.
func TestMain(m *testing.M) {
	config.FS = fstest.MapFS{
		"config.ini": &fstest.MapFile{Data: []byte("[sr.ht]\nsite-name=srht.example\n")},
	}
	config.LoadConfig()
	ecoretest.InitCrypto()
	os.Exit(m.Run())
}

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

// checkInternalAuth runs the real receiving end over the header the hook minted
// — the same internalauth.Identify, against the same pinned ids, that web's
// guard on /internal/repos runs — and returns the caller it identified.
//
// It is the guard itself rather than a decode written here on purpose: this is
// the one test in the repo where both ends of the protocol are present, so it
// is where a drift between them has to fail.
func checkInternalAuth(t *testing.T, r *http.Request) internalauth.Auth {
	t.Helper()
	auth, err := internalauth.Identify(r, core.InternalClientID, core.InternalNodeID)
	require.NoError(t, err, "the header the hook minted must satisfy the guard")
	return auth
}

func TestCreateCompanionSignsAndAnnouncesOnCreate(t *testing.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)
		}
		auth := checkInternalAuth(t, r)
		// The call is made on the pushing owner's behalf, so the receiving
		// service can see whose push provoked it.
		assert.Equal(t, "alice", auth.Name)
		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) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		checkInternalAuth(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) {
	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)
		}
	}
}