v2.30.0-pre2 f03ab25

push_image_ops_test.go RawHistoryFolder

1package cli
2
3import (
4	"context"
5	"encoding/json"
6	"net/http"
7	"net/http/httptest"
8	"testing"
9
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/require"
12
13	"github.com/bnema/gordon/internal/adapters/dto"
14	"github.com/bnema/gordon/internal/adapters/in/cli/remote"
15)
16
17// TestExchangeRegistryAuth_Success verifies the happy path:
18// VerifyAuth is called, then ExchangeRegistryToken is called with the
19// subject from VerifyAuth, and the result is "Bearer <short-lived-token>".
20func TestExchangeRegistryAuth_Success(t *testing.T) {
21	const subject = "ci-bot"
22	const shortToken = "short-lived-token-abc123"
23
24	verifyHandled := false
25	tokenHandled := false
26
27	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
28		switch r.URL.Path {
29		case "/admin/auth/verify":
30			verifyHandled = true
31			w.Header().Set("Content-Type", "application/json")
32			resp := dto.AuthVerifyResponse{
33				Valid:   true,
34				Subject: subject,
35				Scopes:  []string{"push", "pull"},
36			}
37			json.NewEncoder(w).Encode(resp) //nolint:errcheck
38		case "/auth/token":
39			tokenHandled = true
40			// Verify basic auth was set with subject as username
41			u, p, ok := r.BasicAuth()
42			assert.True(t, ok, "expected basic auth")
43			assert.Equal(t, subject, u, "expected subject as username")
44			assert.NotEmpty(t, p, "expected token as password")
45
46			w.Header().Set("Content-Type", "application/json")
47			resp := dto.TokenResponse{Token: shortToken}
48			json.NewEncoder(w).Encode(resp) //nolint:errcheck
49		default:
50			http.NotFound(w, r)
51		}
52	}))
53	defer srv.Close()
54
55	client := remote.NewClient(srv.URL, remote.WithToken("long-lived-token"))
56	ops := &dockerImageOps{
57		remoteClient: client,
58	}
59
60	got, err := ops.exchangeRegistryAuth(context.Background())
61
62	require.NoError(t, err)
63	assert.Equal(t, "Bearer "+shortToken, got)
64	assert.True(t, verifyHandled, "expected /admin/auth/verify to be called")
65	assert.True(t, tokenHandled, "expected /auth/token to be called")
66}
67
68// TestExchangeRegistryAuth_VerifyAuthFails verifies that when VerifyAuth fails,
69// exchangeRegistryAuth returns an error.
70func TestExchangeRegistryAuth_VerifyAuthFails(t *testing.T) {
71	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
72		if r.URL.Path == "/admin/auth/verify" {
73			http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
74			return
75		}
76		http.NotFound(w, r)
77	}))
78	defer srv.Close()
79
80	client := remote.NewClient(srv.URL, remote.WithToken("bad-token"))
81	ops := &dockerImageOps{
82		remoteClient: client,
83	}
84
85	_, err := ops.exchangeRegistryAuth(context.Background())
86
87	require.Error(t, err)
88	assert.Contains(t, err.Error(), "failed to verify token")
89}
90
91// TestExchangeRegistryAuth_TokenNotValid verifies that when VerifyAuth returns
92// Valid=false, exchangeRegistryAuth returns an error.
93func TestExchangeRegistryAuth_TokenNotValid(t *testing.T) {
94	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
95		if r.URL.Path == "/admin/auth/verify" {
96			w.Header().Set("Content-Type", "application/json")
97			resp := dto.AuthVerifyResponse{Valid: false}
98			json.NewEncoder(w).Encode(resp) //nolint:errcheck
99			return
100		}
101		http.NotFound(w, r)
102	}))
103	defer srv.Close()
104
105	client := remote.NewClient(srv.URL, remote.WithToken("expired-token"))
106	ops := &dockerImageOps{
107		remoteClient: client,
108	}
109
110	_, err := ops.exchangeRegistryAuth(context.Background())
111
112	require.Error(t, err)
113	assert.Contains(t, err.Error(), "token is not valid")
114}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close