main 1fb2421

server_test.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package rpc
4
5import (
6	"context"
7	"slices"
8	"testing"
9
10	"net/http/httptest"
11
12	"connectrpc.com/connect"
13	"github.com/stretchr/testify/assert"
14	"github.com/stretchr/testify/require"
15
16	pb "bnema.dev/ermite-api/gen/ermite"
17	"bnema.dev/ermite-api/gen/ermite/ermiteconnect"
18	"bnema.dev/ermite/internal/domain"
19	"bnema.dev/ermite/internal/ports/mocks"
20)
21
22func TestServerServiceGetVersion(t *testing.T) {
23	t.Parallel()
24	advertised := []string{"one", "two"}
25	svc := &serverService{version: "v1.2.3", features: advertised, key: mocks.NewSigningKeyService(t)}
26	resp, err := svc.GetVersion(context.Background(), connect.NewRequest(&pb.GetVersionRequest{}))
27	require.NoError(t, err)
28	assert.Equal(t, "v1.2.3", resp.Msg.GetVersion())
29	assert.Equal(t, []string{"one", "two"}, resp.Msg.GetFeatures())
30
31	// The caller gets a copy: changing it does not change what is advertised.
32	resp.Msg.Features[0] = "tampered"
33	assert.Equal(t, []string{"one", "two"}, advertised)
34}
35
36func TestServerServiceGetSigningKey(t *testing.T) {
37	t.Parallel()
38	key := mocks.NewSigningKeyService(t)
39	key.EXPECT().Info().Return(domain.SigningKeyInfo{
40		Key:       domain.SigningPublicKey{AuthorizedKey: "ssh-ed25519 AAAA", Fingerprint: "SHA256:xyz"},
41		Committer: domain.Identity{Name: "ermite", Email: "ermite@example.com"},
42	}).Once()
43	resp, err := (&serverService{key: key}).GetSigningKey(context.Background(), connect.NewRequest(&pb.GetSigningKeyRequest{}))
44	require.NoError(t, err)
45	assert.Equal(t, "ssh-ed25519 AAAA", resp.Msg.GetPublicKey())
46	assert.Equal(t, "SHA256:xyz", resp.Msg.GetFingerprint())
47	assert.Equal(t, "ermite", resp.Msg.GetCommitterName())
48	assert.Equal(t, "ermite@example.com", resp.Msg.GetCommitterEmail())
49	assert.Equal(t, "ermite@example.com ssh-ed25519 AAAA", resp.Msg.GetAllowedSigners())
50}
51
52// An API built without an OIDC key service answers unimplemented instead of
53// panicking, for the owner and for anyone else.
54func TestRotateOIDCKeyWithoutService(t *testing.T) {
55	t.Parallel()
56	for _, p := range []domain.Principal{domain.Owner(), {}} {
57		ctx := domain.ContextWithPrincipal(context.Background(), p)
58		_, err := (&serverService{}).RotateOIDCKey(ctx, connect.NewRequest(&pb.RotateOIDCKeyRequest{}))
59		assert.Equal(t, connect.CodeUnimplemented, connect.CodeOf(err))
60	}
61}
62
63// GetVersion lists "passkeys" only when the API is built with passkeys.
64func TestAPIListsPasskeysFeatureWhenAvailable(t *testing.T) {
65	t.Parallel()
66	list := func(cfg Config) []string {
67		cfg.Sessions = mocks.NewSessionService(t)
68		cfg.SigningKey = mocks.NewSigningKeyService(t)
69		srv := httptest.NewServer(New(cfg).Handler())
70		t.Cleanup(srv.Close)
71		c := ermiteconnect.NewServerServiceClient(srv.Client(), srv.URL)
72		resp, err := c.GetVersion(context.Background(), connect.NewRequest(&pb.GetVersionRequest{}))
73		require.NoError(t, err)
74		return resp.Msg.GetFeatures()
75	}
76	assert.Equal(t, Features, list(Config{}))
77	got := list(Config{Passkeys: mocks.NewPasskeyService(t)})
78	assert.Equal(t, append(slices.Clone(Features), "passkeys"), got)
79	assert.NotContains(t, Features, "passkeys", "the shared list is not modified")
80}

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 2

Tags 45

↑↓ moveenter openesc close