user_test.go
4536 bytes
1package backend
2
3import (
4 "context"
5 "errors"
6 "os"
7 "testing"
8
9 "github.com/charmbracelet/soft-serve/pkg/access"
10 "github.com/charmbracelet/soft-serve/pkg/config"
11 "github.com/charmbracelet/soft-serve/pkg/db"
12 "github.com/charmbracelet/soft-serve/pkg/proto"
13 "github.com/charmbracelet/soft-serve/pkg/store"
14 "github.com/matryer/is"
15)
16
17// TestAccessLevelCollaboratorPrivateRepo verifies that the anonymous access
18// floor does not raise a collaborator's explicit level on a private
19// repository, while still applying on public repositories.
20func TestAccessLevelCollaboratorPrivateRepo(t *testing.T) {
21 tests := []struct {
22 name string
23 private bool
24 anon access.AccessLevel
25 collab access.AccessLevel
26 want access.AccessLevel
27 }{
28 {"private no-access collab, anon read-only", true, access.ReadOnlyAccess, access.NoAccess, access.NoAccess},
29 {"private read-only collab, anon read-write", true, access.ReadWriteAccess, access.ReadOnlyAccess, access.ReadOnlyAccess},
30 {"private read-write collab, anon read-only", true, access.ReadOnlyAccess, access.ReadWriteAccess, access.ReadWriteAccess},
31 {"public no-access collab, anon read-only", false, access.ReadOnlyAccess, access.NoAccess, access.ReadOnlyAccess},
32 {"public read-only collab, anon read-write", false, access.ReadWriteAccess, access.ReadOnlyAccess, access.ReadWriteAccess},
33 {"public no-access collab, anon no-access", false, access.NoAccess, access.NoAccess, access.NoAccess},
34 }
35
36 for _, tt := range tests {
37 t.Run(tt.name, func(t *testing.T) {
38 is := is.New(t)
39 be, _ := newTestBackend(t)
40 ctx := context.Background()
41
42 is.NoErr(be.SetAnonAccess(ctx, tt.anon))
43
44 _, err := be.CreateRepository(ctx, "repo", nil, proto.RepositoryOptions{Private: tt.private})
45 is.NoErr(err)
46
47 user, err := be.CreateUser(ctx, "collab", proto.UserOptions{})
48 is.NoErr(err)
49 is.NoErr(be.db.TransactionContext(ctx, func(tx *db.Tx) error {
50 return be.store.AddCollabByUsernameAndRepo(ctx, tx, "collab", "repo", tt.collab)
51 }))
52
53 is.Equal(be.AccessLevelForUser(ctx, "repo", user), tt.want)
54 })
55 }
56}
57
58// TestAccessLevelPrivateRepoNonCollaborator verifies that a signed-in user
59// who is not a collaborator has no access to a private repository.
60func TestAccessLevelPrivateRepoNonCollaborator(t *testing.T) {
61 is := is.New(t)
62 be, _ := newTestBackend(t)
63 ctx := context.Background()
64
65 _, err := be.CreateRepository(ctx, "repo", nil, proto.RepositoryOptions{Private: true})
66 is.NoErr(err)
67
68 user, err := be.CreateUser(ctx, "stranger", proto.UserOptions{})
69 is.NoErr(err)
70
71 is.Equal(be.AccessLevelForUser(ctx, "repo", user), access.NoAccess)
72 is.Equal(be.AccessLevelForUser(ctx, "repo", nil), access.NoAccess)
73}
74
75// TestDeleteUserWithRepositories verifies that a user who owns
76// repositories is not deleted until those repositories are gone, and that
77// their repositories are left untouched by the refusal.
78func TestDeleteUserWithRepositories(t *testing.T) {
79 is := is.New(t)
80 be, cfg := newTestBackend(t)
81
82 ctx := context.Background()
83 ctx = config.WithContext(ctx, cfg)
84 ctx = db.WithContext(ctx, be.db)
85 ctx = store.WithContext(ctx, be.store)
86 admin, err := be.User(ctx, "admin")
87 is.NoErr(err)
88 ctx = proto.WithUserContext(ctx, admin)
89
90 owner, err := be.CreateUser(ctx, "owner", proto.UserOptions{})
91 is.NoErr(err)
92 _, err = be.CreateRepository(ctx, "owned", owner, proto.RepositoryOptions{})
93 is.NoErr(err)
94
95 err = be.DeleteUser(ctx, "owner")
96 is.True(errors.Is(err, proto.ErrUserOwnsRepos))
97 _, err = be.User(ctx, "owner")
98 is.NoErr(err)
99 _, err = be.Repository(ctx, "owned")
100 is.NoErr(err)
101
102 is.NoErr(be.DeleteRepository(ctx, "owned"))
103 is.NoErr(be.DeleteUser(ctx, "owner"))
104 _, err = be.User(ctx, "owner")
105 is.True(errors.Is(err, proto.ErrUserNotFound))
106
107 is.True(errors.Is(be.DeleteUser(ctx, "owner"), proto.ErrUserNotFound))
108}
109
110// TestDeleteRepositoryWithoutDirectory verifies that a repository whose
111// directory is gone can still be deleted, and that deleting a repository
112// that does not exist at all reports it as not found.
113func TestDeleteRepositoryWithoutDirectory(t *testing.T) {
114 is := is.New(t)
115 be, cfg := newTestBackend(t)
116
117 ctx := context.Background()
118 ctx = config.WithContext(ctx, cfg)
119 ctx = db.WithContext(ctx, be.db)
120 ctx = store.WithContext(ctx, be.store)
121
122 _, err := be.CreateRepository(ctx, "orphan", nil, proto.RepositoryOptions{})
123 is.NoErr(err)
124 is.NoErr(os.RemoveAll(be.repoPath("orphan")))
125
126 is.NoErr(be.DeleteRepository(ctx, "orphan"))
127 is.True(errors.Is(be.DeleteRepository(ctx, "orphan"), proto.ErrRepoNotFound))
128}