21 files changed,
84 insertions(+),
50 deletions(-)
Author:
Smirnov Oleksandr
ss2316544@gmail.com
Committed by:
GitHub
noreply@github.com
Committed at:
2024-09-12 14:20:21 +0300
Parent:
0c026f2
jump to
M
.golangci.yaml
··· 70 70 - usestdlibvars # detects the possibility to use variables/constants from the Go standard library 71 71 - wastedassign # finds wasted assignment statements 72 72 - whitespace # detects leading and trailing whitespace 73 + - inamedparam # peports interfaces with unnamed method parameters 74 + - forcetypeassert # finds forced type assertions. 75 + - exhaustruct # checks if all structure fields are initialized. 76 + - err113 # forbids usage of dynamic errors 77 + - contextcheck # check whether the function uses a non-inherited context 78 + - ireturn # accept interfaces, return concrete types 73 79 74 80 linters-settings: 75 81 cyclop: ··· 122 128 nakedret: 123 129 # the gods will judge me but I just don't like naked returns at all 124 130 max-func-lines: 0 131 + 132 + exhaustruct: 133 + exclude: 134 + - 'log/slog\.HandlerOptions' 135 + - 'net/http\.Server' 136 + 137 + - 'github.com/golang-jwt/jwt/v5\.RegisteredClaims' 125 138 126 139 issues: 127 140 # Maximum count of issues with the same text.
M
e2e/apiv1_auth_test.go
··· 1 -package e2e 1 +package e2e_test 2 2 3 3 import ( 4 4 "net/http" ··· 47 47 {name: "all fiels empty", email: "", password: "", username: ""}, 48 48 { 49 49 name: "non valid email", 50 + username: "testing", 50 51 email: "email", 51 52 password: "password", 52 53 }, ··· 214 215 unactivatedEmail := e.uuid() + "@test.com" 215 216 e.insertUserIntoDB(e.uuid(), unactivatedEmail, password, false) 216 217 218 + //exhaustruct:ignore 217 219 tests := []struct { 218 220 name string 219 221 email string
M
e2e/apiv1_notes_test.go
··· 1 -package e2e 1 +package e2e_test 2 2 3 3 import ( 4 4 "net/http" ··· 28 28 }{ 29 29 { 30 30 name: "empty request", 31 - inp: apiv1NoteCreateRequest{}, 31 + inp: apiv1NoteCreateRequest{}, //nolint:exhaustruct 32 32 assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) { 33 33 e.Equal(r.Code, http.StatusBadRequest) 34 34 }, 35 35 }, 36 36 { 37 37 name: "content only", 38 - inp: apiv1NoteCreateRequest{Content: e.uuid()}, 38 + inp: apiv1NoteCreateRequest{Content: e.uuid()}, //nolint:exhaustruct 39 39 assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) { 40 40 e.Equal(r.Code, http.StatusCreated) 41 41 ··· 51 51 }, 52 52 { 53 53 name: "set slug", 54 - inp: apiv1NoteCreateRequest{ 54 + inp: apiv1NoteCreateRequest{ //nolint:exhaustruct 55 55 Slug: e.uuid() + "fuker", 56 56 Content: e.uuid(), 57 57 }, ··· 67 67 }, 68 68 { 69 69 name: "all possible fields", 70 - inp: apiv1NoteCreateRequest{ 70 + inp: apiv1NoteCreateRequest{ //nolint:exhaustruct 71 71 Content: e.uuid(), 72 72 BurnBeforeExpiration: true, 73 73 ExpiresAt: time.Now().Add(time.Hour), ··· 96 96 97 97 func (e *AppTestSuite) TestNoteV1_Create_authorized() { 98 98 uid, toks := e.createAndSingIn(e.uuid()+"@test.com", e.uuid(), "password") 99 - httpResp := e.httpRequest(http.MethodPost, "/api/v1/note", e.jsonify(apiv1NoteCreateRequest{ 100 - Content: "some random ass content for the test", 101 - }), toks.AccessToken) 99 + httpResp := e.httpRequest( 100 + http.MethodPost, 101 + "/api/v1/note", 102 + e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct 103 + Content: "some random ass content for the test", 104 + }), 105 + toks.AccessToken, 106 + ) 102 107 103 108 var body apiv1NoteCreateResponse 104 109 e.readBodyAndUnjsonify(httpResp.Body, &body) ··· 118 123 119 124 func (e *AppTestSuite) TestNoteV1_Get() { 120 125 content := e.uuid() 121 - httpResp := e.httpRequest(http.MethodPost, "/api/v1/note", e.jsonify(apiv1NoteCreateRequest{ 122 - Content: content, 123 - })) 126 + httpResp := e.httpRequest( 127 + http.MethodPost, 128 + "/api/v1/note", 129 + e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct 130 + Content: content, 131 + }), 132 + ) 124 133 e.Equal(http.StatusCreated, httpResp.Code) 125 134 126 135 var bodyCreated apiv1NoteCreateResponse
M
e2e/e2e_test.go
··· 1 -package e2e 1 +package e2e_test 2 2 3 3 import ( 4 4 "context" ··· 152 152 153 153 // run migrations 154 154 sdb := stdlib.OpenDBFromPool(db.Pool) 155 - driver, err := pgx.WithInstance(sdb, &pgx.Config{}) 155 + driver, err := pgx.WithInstance(sdb, &pgx.Config{}) //nolint:exhaustruct 156 156 e.require.NoError(err) 157 157 158 158 m, err := migrate.NewWithDatabaseInstance( ··· 175 175 } 176 176 177 177 func (e *AppTestSuite) getConfig() *config.Config { 178 - return &config.Config{ 178 + return &config.Config{ //nolint:exhaustruct 179 179 AppEnv: "testing", 180 180 ServerPort: "3000", 181 181 PasswordSalt: "salty-password",
M
e2e/e2e_utils_db_test.go
··· 1 -package e2e 1 +package e2e_test 2 2 3 3 import ( 4 4 "errors" ··· 63 63 err = e.postgresDB.QueryRow(e.ctx, query, args...). 64 64 Scan(&session.RefreshToken, &session.ExpiresAt) 65 65 if errors.Is(err, pgx.ErrNoRows) { 66 - return models.Session{} 66 + return models.Session{} //nolint:exhaustruct 67 67 } 68 68 69 69 e.require.NoError(err) ··· 84 84 err = e.postgresDB.QueryRow(e.ctx, query, args...). 85 85 Scan(&u.ID, &u.Username, &u.Activated, &u.Email, &u.Password) 86 86 if errors.Is(err, pgx.ErrNoRows) { 87 - return models.User{} 87 + return models.User{} //nolint:exhaustruct 88 88 } 89 89 90 90 e.require.NoError(err) ··· 103 103 err = e.postgresDB.QueryRow(e.ctx, query, args...). 104 104 Scan(¬e.ID, ¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, ¬e.CreatedAt, ¬e.ExpiresAt) 105 105 if errors.Is(err, pgx.ErrNoRows) { 106 - return models.Note{} 106 + return models.Note{} //nolint:exhaustruct 107 107 } 108 108 109 109 e.require.NoError(err) ··· 128 128 var na noteAuthorModel 129 129 err = e.postgresDB.QueryRow(e.ctx, qeuery, args...).Scan(&na.noteID, &na.userID) 130 130 if errors.Is(err, pgx.ErrNoRows) { 131 - return noteAuthorModel{} 131 + return noteAuthorModel{} //nolint:exhaustruct 132 132 } 133 133 134 134 e.require.NoError(err)
M
internal/config/config.go
··· 68 68 func mustParseDurationOrPanic(dur string) time.Duration { 69 69 d, err := time.ParseDuration(dur) 70 70 if err != nil { 71 - panic(errors.Join(errors.New("cannot time.ParseDuration"), err)) 71 + panic(errors.Join(errors.New("cannot time.ParseDuration"), err)) //nolint:err113 72 72 } 73 73 74 74 return d
M
internal/jwtutil/jwtutil.go
··· 9 9 "github.com/golang-jwt/jwt/v5" 10 10 ) 11 11 12 +var ErrUnexpectedSigningMethod = errors.New("unexpected signing method") 13 + 12 14 type JWTTokenizer interface { 13 15 // AccessToken generates a new access token with the given payload 14 16 AccessToken(pl Payload) (string, error) ··· 58 60 var claims jwt.RegisteredClaims 59 61 _, err := jwt.ParseWithClaims(token, &claims, func(t *jwt.Token) (interface{}, error) { 60 62 if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { 61 - return nil, errors.New("unexpected signing method") 63 + return nil, ErrUnexpectedSigningMethod 62 64 } 63 65 return []byte(j.signingKey), nil 64 66 })
M
internal/mailer/testing_mailer_test.go
··· 20 20 } 21 21 22 22 func TestMailer_GetLastSentEmailToEmail(t *testing.T) { 23 + email := "test@mail.com" 24 + content := "content" 25 + 23 26 m := NewTestMailer() 24 27 assert.Empty(t, m.emails) 25 28 26 - email := "test@mail.com" 27 - content := "content" 28 - err := m.Send(context.TODO(), email, "", content) 29 - require.NoError(t, err) 29 + m.emails[email] = content 30 30 31 31 c := m.GetLastSentEmailToEmail(email) 32 32 assert.Equal(t, content, c)
M
internal/models/notes_test.go
··· 17 17 // NOTE: there no need to test if note is expired since it tested in IsExpired test 18 18 { 19 19 name: "ok", 20 - note: Note{ 20 + note: Note{ //nolint:exhaustruct 21 21 Content: "some wired ass content", 22 22 ExpiresAt: time.Now().Add(time.Hour), 23 23 }, 24 24 willError: false, 25 + error: nil, 25 26 }, 26 27 { 27 28 name: "content missing", 28 - note: Note{Content: ""}, 29 + note: Note{Content: ""}, //nolint:exhaustruct 29 30 willError: true, 30 31 error: ErrNoteContentIsEmpty, 31 32 }, ··· 51 52 }{ 52 53 { 53 54 name: "expired", 54 - note: Note{ExpiresAt: time.Now().Add(-time.Hour)}, 55 + note: Note{ExpiresAt: time.Now().Add(-time.Hour)}, //nolint:exhaustruct 55 56 expected: true, 56 57 }, 57 58 { 58 59 name: "not expired", 59 - note: Note{ExpiresAt: time.Now().Add(time.Hour)}, 60 + note: Note{ExpiresAt: time.Now().Add(time.Hour)}, //nolint:exhaustruct 60 61 expected: false, 61 62 }, 62 63 { 63 64 name: "zero expiration", 64 - note: Note{ExpiresAt: time.Time{}}, 65 + note: Note{ExpiresAt: time.Time{}}, //nolint:exhaustruct 65 66 expected: false, 66 67 }, 67 68 } ··· 81 82 }{ 82 83 { 83 84 name: "should be burnt", 84 - note: Note{ 85 + note: Note{ //nolint:exhaustruct 85 86 BurnBeforeExpiration: true, 86 87 ExpiresAt: time.Now().Add(time.Hour), 87 88 }, ··· 89 90 }, 90 91 { 91 92 name: "could not be burnt, no expiration time", 92 - note: Note{ 93 + note: Note{ //nolint:exhaustruct 93 94 BurnBeforeExpiration: true, 94 95 ExpiresAt: time.Time{}, 95 96 }, ··· 97 98 }, 98 99 { 99 100 name: "could not be burnt, burn when expiration and burn is false", 100 - note: Note{ 101 + note: Note{ //nolint:exhaustruct 101 102 BurnBeforeExpiration: false, 102 103 ExpiresAt: time.Time{}, 103 104 },
M
internal/models/user.go
··· 34 34 // NOTE: there's probably a better way to validate emails 35 35 _, err := mail.ParseAddress(u.Email) 36 36 if err != nil { 37 - return errors.New("user: invalid email") 37 + return errors.New("user: invalid email") //nolint:err113 38 38 } 39 39 40 40 if len(u.Password) < 6 { 41 - return errors.New("user: password too short, minimum 6 chars") 41 + return errors.New("user: password too short, minimum 6 chars") //nolint:err113 42 42 } 43 43 44 44 if len(u.Username) == 0 { 45 - return errors.New("user: username is required") 45 + return errors.New("user: username is required") //nolint:err113 46 46 } 47 47 48 48 return nil
M
internal/service/notesrv/notesrv.go
··· 23 23 noterepo noterepo.NoteStorer 24 24 } 25 25 26 -func New(noterepo noterepo.NoteStorer) NoteServicer { 26 +func New(noterepo noterepo.NoteStorer) *NoteSrv { 27 27 return &NoteSrv{ 28 28 noterepo: noterepo, 29 29 } ··· 62 62 } 63 63 64 64 // TODO: there should be a better way to do it 65 - m := models.Note{ 65 + m := models.Note{ //nolint:exhaustruct 66 66 ExpiresAt: note.ExpiresAt, 67 67 BurnBeforeExpiration: note.BurnBeforeExpiration, 68 68 }
M
internal/service/usersrv/usersrv.go
··· 55 55 jwtTokenizer jwtutil.JWTTokenizer, 56 56 mailer mailer.Mailer, 57 57 refreshTokenTTL, verificationTokenTTL time.Duration, 58 -) UserServicer { 58 +) *UserSrv { 59 59 return &UserSrv{ 60 60 userstore: userstore, 61 61 sessionstore: sessionstore, ··· 93 93 // TODO: handle the error that might be returned 94 94 // i dont think that tehre's need to handle the error, just log it 95 95 bgCtx, bgCancel := context.WithTimeout(context.Background(), 10*time.Second) 96 - go u.sendVerificationEmail(bgCtx, bgCancel, inp.Email, vtok) //nolint:errcheck 96 + go u.sendVerificationEmail(bgCtx, bgCancel, inp.Email, vtok) //nolint:errcheck,contextcheck 97 97 98 98 return uid, nil 99 99 } ··· 211 211 } 212 212 213 213 bgCtx, bgCancel := context.WithTimeout(context.Background(), 10*time.Second) 214 - go u.sendVerificationEmail(bgCtx, bgCancel, inp.Email, token) //nolint:errcheck 214 + go u.sendVerificationEmail(bgCtx, bgCancel, inp.Email, token) //nolint:errcheck,contextcheck 215 215 216 216 return nil 217 217 }
M
internal/transport/http/apiv1/middleware.go
··· 82 82 if !exists { 83 83 return uuid.Nil 84 84 } 85 - return userID.(uuid.UUID) 85 + 86 + uid, ok := userID.(uuid.UUID) 87 + if !ok { 88 + return uuid.Nil 89 + } 90 + 91 + return uid 86 92 } 87 93 88 94 func (a *APIV1) validateAuthorizedUser(ctx context.Context, accessToken string) (uuid.UUID, error) {