1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package app
4
5import (
6 "errors"
7 "strings"
8 "testing"
9
10 "connectrpc.com/connect"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13
14 "bnema.dev/ermite-api/gen/ermite"
15
16 "bnema.dev/ermite/internal/adapters/client"
17)
18
19// allFeatures are the features this client uses.
20var allFeatures = []string{"repo.update", "events.resume", "merge_requests", "merge_requests.delete_branch", "signing_key", "session", "token.branch_prefixes", "ci.runs", "ci.runners", "ci.secrets", "ci.dispatch", "passkeys"}
21
22// Every feature the client uses needs a sentence for the version warning.
23func TestEveryUsedFeatureExplainsWhatBreaks(t *testing.T) {
24 for _, f := range client.UsedFeatures() {
25 assert.NotEmpty(t, featureUse[f], f)
26 }
27}
28
29func versionFake(features ...string) *fakeAPI {
30 return &fakeAPI{getVersion: func(*ermite.GetVersionRequest) (*ermite.GetVersionResponse, error) {
31 return &ermite.GetVersionResponse{Version: "v0.4.0", Features: features}, nil
32 }}
33}
34
35func TestVersionClientOnlyWithoutURL(t *testing.T) {
36 res := runCLI(t, nil, "", "version").ok(t)
37 assert.Equal(t, "ermite v0.0.1\n", res.stdout)
38 assert.Empty(t, res.stderr)
39
40 res = runCLI(t, nil, "", "version", "--json").ok(t)
41 assert.JSONEq(t, `{"client":{"version":"v0.0.1"},"server":null}`, res.stdout)
42}
43
44func TestVersionWithServer(t *testing.T) {
45 f := versionFake(allFeatures...)
46 url := f.serve(t)
47 res := runCLI(t, envFor(url), "", "version").ok(t)
48 assert.Equal(t, "client ermite v0.0.1\nserver v0.4.0 at "+url+"\nfeatures repo.update, events.resume, merge_requests, merge_requests.delete_branch, signing_key, session, token.branch_prefixes, ci.runs, ci.runners, ci.secrets, ci.dispatch, passkeys\n", res.stdout)
49 assert.Empty(t, res.stderr, "no warning when every feature is there")
50
51 res = runCLI(t, envFor(url), "", "version", "--json").ok(t)
52 assert.JSONEq(t, `{"client":{"version":"v0.0.1"},"server":{"url":"`+url+`","version":"v0.4.0","features":["repo.update","events.resume","merge_requests","merge_requests.delete_branch","signing_key","session","token.branch_prefixes","ci.runs","ci.runners","ci.secrets","ci.dispatch","passkeys"],"missing_features":[]}}`, res.stdout)
53}
54
55func TestVersionWarnsAboutMissingFeatures(t *testing.T) {
56 f := versionFake("repo.update", "merge_requests", "merge_requests.delete_branch", "signing_key", "session", "token.branch_prefixes", "ci.runs", "ci.runners", "ci.secrets", "ci.dispatch", "passkeys")
57 url := f.serve(t)
58 res := runCLI(t, envFor(url), "", "version").ok(t)
59 assert.Contains(t, res.stdout, "v0.4.0")
60 assert.Contains(t, res.stderr, "ermite: warning: the server does not support events.resume")
61 assert.NotContains(t, res.stderr, "repo.update")
62
63 res = runCLI(t, envFor(url), "", "version", "--json").ok(t)
64 assert.JSONEq(t, `{"client":{"version":"v0.0.1"},"server":{"url":"`+url+`","version":"v0.4.0","features":["repo.update","merge_requests","merge_requests.delete_branch","signing_key","session","token.branch_prefixes","ci.runs","ci.runners","ci.secrets","ci.dispatch","passkeys"],"missing_features":["events.resume"]}}`, res.stdout)
65 assert.Contains(t, res.stderr, "events.resume", "the warning also goes to stderr with --json")
66
67 f = versionFake()
68 res = runCLI(t, envFor(f.serve(t)), "", "version").ok(t)
69 assert.Contains(t, res.stderr, "repo.update")
70 assert.Contains(t, res.stderr, "events.resume")
71 assert.Contains(t, res.stderr, "passkeys")
72}
73
74// A server without passkeys (http public URL, or an IP address host) works for
75// everything else: only `ermite web passkey` is warned about.
76func TestVersionWarnsAboutMissingPasskeys(t *testing.T) {
77 f := versionFake(allFeatures[:len(allFeatures)-1]...)
78 res := runCLI(t, envFor(f.serve(t)), "", "version", "--json").ok(t)
79 assert.Contains(t, res.stdout, `"missing_features": [
80 "passkeys"
81 ]`)
82 assert.Equal(t, 1, strings.Count(res.stderr, "warning:"), res.stderr)
83 assert.Contains(t, res.stderr, "the server does not support passkeys, which `ermite web passkey` needs")
84 assert.Contains(t, res.stderr, "https public URL")
85}
86
87func TestVersionServerFailureStillShowsClient(t *testing.T) {
88 f := &fakeAPI{getVersion: func(*ermite.GetVersionRequest) (*ermite.GetVersionResponse, error) {
89 return nil, connect.NewError(connect.CodeInternal, errors.New("boom"))
90 }}
91 res := runCLI(t, envFor(f.serve(t)), "", "version")
92 require.Error(t, res.err)
93 assert.Equal(t, "ermite v0.0.1\n", res.stdout)
94 assert.Contains(t, res.err.Error(), "server error: boom")
95}
96
97func TestVersionWorksWithoutToken(t *testing.T) {
98 f := versionFake(allFeatures...)
99 runCLI(t, map[string]string{"ERMITE_URL": f.serve(t)}, "", "version").ok(t)
100 assert.Empty(t, f.header[0].Values("Authorization"))
101}