Monorepo for Tangled tangled.org
2

Configure Feed

Select the types of activity you want to include in your feed.

at icy/qmlqxq 5.8 kB View raw
1package state 2 3import ( 4 "context" 5 "database/sql" 6 "log/slog" 7 "net/http" 8 "net/http/httptest" 9 "testing" 10 11 "github.com/go-chi/chi/v5" 12 "tangled.org/core/appview/config" 13 "tangled.org/core/appview/models" 14) 15 16// routerFixture builds a minimal chi router wired to newDispatchHandler with 17// spy handlers. userPath and stdPath are set to the URL.Path received by 18// the respective spy after ServeHTTP returns. 19func routerFixture(cfg *config.Config, repoDB fakeRepoDB) (router http.Handler, userPath *string, stdPath *string) { 20 up := "" 21 sp := "" 22 userRouter := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 23 up = r.URL.Path 24 w.WriteHeader(http.StatusOK) 25 }) 26 stdRouter := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 27 sp = r.URL.Path 28 w.WriteHeader(http.StatusOK) 29 }) 30 31 r := chi.NewRouter() 32 r.HandleFunc("/*", newDispatchHandler(cfg, repoDB, slog.Default(), userRouter, stdRouter)) 33 return r, &up, &sp 34} 35 36// fakeRepoDB implements db.Execer. The nil value is safe to use for test 37// cases that don't exercise the DID-resolution path. 38type fakeRepoDB map[string]*models.Repo 39 40func (f fakeRepoDB) Exec(query string, args ...any) (sql.Result, error) { return nil, nil } 41func (f fakeRepoDB) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) { 42 return nil, nil 43} 44func (f fakeRepoDB) Query(query string, args ...any) (*sql.Rows, error) { return nil, nil } 45func (f fakeRepoDB) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) { 46 return nil, nil 47} 48func (f fakeRepoDB) QueryRow(query string, args ...any) *sql.Row { return nil } 49func (f fakeRepoDB) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row { 50 return nil 51} 52func (f fakeRepoDB) Prepare(query string) (*sql.Stmt, error) { return nil, nil } 53func (f fakeRepoDB) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { 54 return nil, nil 55} 56 57// normalCfg returns a config with project mode disabled. 58func normalCfg() *config.Config { 59 return &config.Config{} 60} 61 62// projectCfg returns a config with project mode enabled for the given user. 63func projectCfg(user string) *config.Config { 64 return &config.Config{ 65 Project: config.ProjectConfig{ 66 Enabled: true, 67 User: user, 68 }, 69 } 70} 71 72func TestDispatch_NormalMode(t *testing.T) { 73 cases := []struct { 74 name string 75 path string 76 wantUser string // expected URL seen by userRouter; empty means standardRouter should be hit 77 wantStd string // expected URL seen by standardRouter; empty means userRouter should be hit 78 wantCode int // expected HTTP status; 0 means 200 79 }{ 80 {"handle", "/user.com/my-repo", "/user.com/my-repo", "", 0}, 81 {"handle root", "/user.com", "/user.com", "", 0}, 82 {"@handle redirect", "/@user.com/repo", "", "", http.StatusFound}, 83 {"flattened did redirect", "/did-plc-abc123xyz", "", "", http.StatusFound}, 84 {"settings", "/settings/profile", "", "/settings/profile", 0}, 85 {"login", "/login", "", "/login", 0}, 86 {"notifications", "/notifications", "", "/notifications", 0}, 87 {"signup", "/signup/complete", "", "/signup/complete", 0}, 88 {"root", "/", "", "/", 0}, 89 {"unknown segment", "/not-a-handle", "", "/not-a-handle", 0}, 90 } 91 92 for _, tc := range cases { 93 t.Run(tc.name, func(t *testing.T) { 94 router, userPath, stdPath := routerFixture(normalCfg(), nil) 95 req := httptest.NewRequest(http.MethodGet, tc.path, nil) 96 rr := httptest.NewRecorder() 97 router.ServeHTTP(rr, req) 98 99 wantCode := tc.wantCode 100 if wantCode == 0 { 101 wantCode = http.StatusOK 102 } 103 if rr.Code != wantCode { 104 t.Errorf("status = %d, want %d", rr.Code, wantCode) 105 } 106 if tc.wantUser != "" && *userPath != tc.wantUser { 107 t.Errorf("userRouter received %q, want %q", *userPath, tc.wantUser) 108 } 109 if tc.wantStd != "" && *stdPath != tc.wantStd { 110 t.Errorf("stdRouter received %q, want %q", *stdPath, tc.wantStd) 111 } 112 if tc.wantCode == http.StatusFound { 113 // neither spy should have been called 114 if *userPath != "" || *stdPath != "" { 115 t.Errorf("redirect case should not reach routers (user=%q std=%q)", *userPath, *stdPath) 116 } 117 } 118 }) 119 } 120} 121 122func TestDispatch_ProjectMode(t *testing.T) { 123 const projectUser = "anirudh.fi" 124 125 cases := []struct { 126 name string 127 path string 128 wantUser string 129 wantStd string 130 wantCode int 131 }{ 132 {"root becomes profile", "/", "/anirudh.fi", "", 0}, 133 {"repo", "/my-repo", "/anirudh.fi/my-repo", "", 0}, 134 {"repo with subpath", "/my-repo/issues/1", "/anirudh.fi/my-repo/issues/1", "", 0}, 135 {"settings prefix", "/settings/profile", "", "/settings/profile", 0}, 136 {"login prefix", "/login", "", "/login", 0}, 137 {"notifications prefix", "/notifications", "", "/notifications", 0}, 138 {"signup is standard prefix", "/signup", "", "/signup", 0}, 139 {"search prefix", "/search", "", "/search", 0}, 140 {"explicit handle still works", "/anirudh.fi/my-repo", "/anirudh.fi/my-repo", "", 0}, 141 {"other user still works", "/other.user/their-repo", "/other.user/their-repo", "", 0}, 142 {"@handle redirect", "/@anirudh.fi/repo", "", "", http.StatusFound}, 143 } 144 145 for _, tc := range cases { 146 t.Run(tc.name, func(t *testing.T) { 147 router, userPath, stdPath := routerFixture(projectCfg(projectUser), nil) 148 req := httptest.NewRequest(http.MethodGet, tc.path, nil) 149 rr := httptest.NewRecorder() 150 router.ServeHTTP(rr, req) 151 152 wantCode := tc.wantCode 153 if wantCode == 0 { 154 wantCode = http.StatusOK 155 } 156 if rr.Code != wantCode { 157 t.Errorf("status = %d, want %d", rr.Code, wantCode) 158 } 159 if tc.wantUser != "" && *userPath != tc.wantUser { 160 t.Errorf("userRouter received %q, want %q", *userPath, tc.wantUser) 161 } 162 if tc.wantStd != "" && *stdPath != tc.wantStd { 163 t.Errorf("stdRouter received %q, want %q", *stdPath, tc.wantStd) 164 } 165 }) 166 } 167}