1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package rpc
4
5import (
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/require"
10
11 pb "bnema.dev/ermite-api/gen/ermite"
12 "bnema.dev/ermite/internal/domain"
13)
14
15// Every value of the domain maps to a real value of the contract, never to
16// UNSPECIFIED, and the mappers that have an inverse round-trip. A value added
17// to the domain and forgotten here would reach clients as "not set".
18func TestEnumMappersCoverEveryDomainValue(t *testing.T) {
19 t.Run("visibility", func(t *testing.T) {
20 for _, v := range []domain.Visibility{domain.VisibilityPublic, domain.VisibilityPrivate} {
21 got := visibilityToPB(v)
22 assert.NotEqual(t, pb.Visibility_VISIBILITY_UNSPECIFIED, got, v)
23 back, err := visibilityFromPB(got)
24 require.NoError(t, err, v)
25 assert.Equal(t, v, back)
26 }
27 assert.Equal(t, pb.Visibility_VISIBILITY_UNSPECIFIED, visibilityToPB(""))
28 _, err := visibilityFromPB(pb.Visibility(99))
29 assert.ErrorIs(t, err, domain.ErrInvalidVisibility)
30 })
31
32 t.Run("scope", func(t *testing.T) {
33 for _, s := range domain.AllScopes() {
34 got, ok := scopeToPB[s]
35 require.True(t, ok, "scope %s has no counterpart in the contract", s)
36 assert.NotEqual(t, pb.Scope_SCOPE_UNSPECIFIED, got, s)
37 back, err := scopeFromPB(got)
38 require.NoError(t, err, s)
39 assert.Equal(t, s, back)
40 }
41 _, err := scopeFromPB(pb.Scope_SCOPE_UNSPECIFIED)
42 assert.ErrorIs(t, err, domain.ErrInvalidScope)
43 // Every scope of the contract is a scope of the domain.
44 for v := range pb.Scope_name {
45 if pb.Scope(v) == pb.Scope_SCOPE_UNSPECIFIED {
46 continue
47 }
48 _, err := scopeFromPB(pb.Scope(v))
49 assert.NoError(t, err, pb.Scope(v))
50 }
51 })
52
53 t.Run("ref kind", func(t *testing.T) {
54 for _, k := range []domain.RefKind{domain.RefKindHead, domain.RefKindBranch, domain.RefKindTag} {
55 got := refKindToPB(k)
56 assert.NotEqual(t, pb.RefKind_REF_KIND_UNSPECIFIED, got, k)
57 back, err := refKindFromPB(got)
58 require.NoError(t, err, k)
59 assert.Equal(t, k, back)
60 }
61 _, err := refKindFromPB(pb.RefKind_REF_KIND_UNSPECIFIED)
62 assert.ErrorIs(t, err, domain.ErrInvalidRefKind)
63 })
64
65 t.Run("entry kind", func(t *testing.T) {
66 seen := map[pb.EntryKind]domain.EntryKind{}
67 for _, k := range []domain.EntryKind{
68 domain.EntryFile, domain.EntryExecutable, domain.EntryDirectory, domain.EntrySymlink, domain.EntrySubmodule,
69 } {
70 got := entryKindToPB(k)
71 assert.NotEqual(t, pb.EntryKind_ENTRY_KIND_UNSPECIFIED, got, k)
72 assert.NotContains(t, seen, got, "%s and %s share a value", k, seen[got])
73 seen[got] = k
74 }
75 })
76
77 t.Run("file status", func(t *testing.T) {
78 seen := map[pb.FileStatus]domain.FileStatus{}
79 for _, s := range []domain.FileStatus{
80 domain.FileAdded, domain.FileDeleted, domain.FileModified, domain.FileRenamed, domain.FileCopied, domain.FileTypeChanged,
81 } {
82 got := fileStatusToPB(s)
83 assert.NotEqual(t, pb.FileStatus_FILE_STATUS_UNSPECIFIED, got, s)
84 assert.NotContains(t, seen, got, "%s and %s share a value", s, seen[got])
85 seen[got] = s
86 }
87 })
88
89 t.Run("line kind", func(t *testing.T) {
90 seen := map[pb.LineKind]domain.LineKind{}
91 for _, k := range []domain.LineKind{domain.LineContext, domain.LineAddition, domain.LineDeletion} {
92 got := lineKindToPB(k)
93 assert.NotEqual(t, pb.LineKind_LINE_KIND_UNSPECIFIED, got, k)
94 assert.NotContains(t, seen, got, "%s and %s share a value", k, seen[got])
95 seen[got] = k
96 }
97 })
98
99 // Every value of the contract except UNSPECIFIED is produced by some value
100 // of the domain: nothing in the contract is unreachable.
101 t.Run("the contract has no value the domain cannot produce", func(t *testing.T) {
102 produced := func(names map[int32]string, from func(v int32) bool) {
103 for v, n := range names {
104 if v == 0 {
105 continue
106 }
107 assert.True(t, from(v), "%s is never produced", n)
108 }
109 }
110 produced(pb.EntryKind_name, func(v int32) bool {
111 for _, k := range []domain.EntryKind{domain.EntryFile, domain.EntryExecutable, domain.EntryDirectory, domain.EntrySymlink, domain.EntrySubmodule} {
112 if entryKindToPB(k) == pb.EntryKind(v) {
113 return true
114 }
115 }
116 return false
117 })
118 produced(pb.FileStatus_name, func(v int32) bool {
119 for _, k := range []domain.FileStatus{domain.FileAdded, domain.FileDeleted, domain.FileModified, domain.FileRenamed, domain.FileCopied, domain.FileTypeChanged} {
120 if fileStatusToPB(k) == pb.FileStatus(v) {
121 return true
122 }
123 }
124 return false
125 })
126 produced(pb.LineKind_name, func(v int32) bool {
127 for _, k := range []domain.LineKind{domain.LineContext, domain.LineAddition, domain.LineDeletion} {
128 if lineKindToPB(k) == pb.LineKind(v) {
129 return true
130 }
131 }
132 return false
133 })
134 })
135}