mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-12 08:13:01 +03:00
ci/cd: migrate backend linter to v2. fixed unit test workflow (#400)
This commit is contained in:
4
.github/workflows/backend-linter.yml
vendored
4
.github/workflows/backend-linter.yml
vendored
@@ -32,8 +32,8 @@ jobs:
|
|||||||
go-version-file: backend/go.mod
|
go-version-file: backend/go.mod
|
||||||
|
|
||||||
- name: Run Golangci-lint
|
- name: Run Golangci-lint
|
||||||
uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6.5.2
|
uses: golangci/golangci-lint-action@dec74fa03096ff515422f71d18d41307cacde373 # v7.0.0
|
||||||
with:
|
with:
|
||||||
version: v1.64
|
version: v2.0.2
|
||||||
working-directory: backend
|
working-directory: backend
|
||||||
only-new-issues: ${{ github.event_name == 'pull_request' }}
|
only-new-issues: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|||||||
5
.github/workflows/unit-tests.yml
vendored
5
.github/workflows/unit-tests.yml
vendored
@@ -2,11 +2,11 @@ name: Unit Tests
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- "backend/**"
|
- "backend/**"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- "backend/**"
|
- "backend/**"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -25,6 +25,7 @@ jobs:
|
|||||||
- name: Run backend unit tests
|
- name: Run backend unit tests
|
||||||
working-directory: backend
|
working-directory: backend
|
||||||
run: |
|
run: |
|
||||||
|
set -e -o pipefail
|
||||||
go test -v ./... | tee /tmp/TestResults.log
|
go test -v ./... | tee /tmp/TestResults.log
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -41,4 +41,4 @@ EXPOSE 80
|
|||||||
ENV APP_ENV=production
|
ENV APP_ENV=production
|
||||||
|
|
||||||
ENTRYPOINT ["sh", "./scripts/docker/create-user.sh"]
|
ENTRYPOINT ["sh", "./scripts/docker/create-user.sh"]
|
||||||
CMD ["sh", "./scripts/docker/entrypoint.sh"]
|
CMD ["sh", "./scripts/docker/entrypoint.sh"]
|
||||||
|
|||||||
@@ -1,25 +1,64 @@
|
|||||||
|
version: "2"
|
||||||
|
run:
|
||||||
|
tests: true
|
||||||
|
timeout: 5m
|
||||||
linters:
|
linters:
|
||||||
# Disable all linters.
|
default: none
|
||||||
# Default: false
|
|
||||||
disable-all: true
|
|
||||||
enable:
|
enable:
|
||||||
|
- asasalint
|
||||||
|
- asciicheck
|
||||||
|
- bidichk
|
||||||
|
- bodyclose
|
||||||
|
- contextcheck
|
||||||
|
- copyloopvar
|
||||||
|
- durationcheck
|
||||||
- errcheck
|
- errcheck
|
||||||
- gosimple
|
- errchkjson
|
||||||
|
- errorlint
|
||||||
|
- exhaustive
|
||||||
|
- gocheckcompilerdirectives
|
||||||
|
- gochecksumtype
|
||||||
|
- gocognit
|
||||||
|
- gocritic
|
||||||
|
- gosec
|
||||||
|
- gosmopolitan
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
|
- loggercheck
|
||||||
|
- makezero
|
||||||
|
- musttag
|
||||||
|
- nilerr
|
||||||
|
- nilnesserr
|
||||||
|
- noctx
|
||||||
|
- protogetter
|
||||||
|
- reassign
|
||||||
|
- recvcheck
|
||||||
|
- rowserrcheck
|
||||||
|
- spancheck
|
||||||
|
- sqlclosecheck
|
||||||
- staticcheck
|
- staticcheck
|
||||||
|
- testifylint
|
||||||
- unused
|
- unused
|
||||||
- gosec
|
- usestdlibvars
|
||||||
- gocognit
|
- zerologlint
|
||||||
|
|
||||||
presets:
|
|
||||||
- bugs
|
|
||||||
- sql
|
|
||||||
exclusions:
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
paths:
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
- internal/service/test_service.go
|
- internal/service/test_service.go
|
||||||
|
formatters:
|
||||||
run:
|
enable:
|
||||||
timeout: "5m"
|
- goimports
|
||||||
tests: true
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/pocket-id/pocket-id/backend
|
module github.com/pocket-id/pocket-id/backend
|
||||||
|
|
||||||
go 1.23.1
|
go 1.23.7
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/caarlos0/env/v11 v11.3.1
|
github.com/caarlos0/env/v11 v11.3.1
|
||||||
|
|||||||
@@ -240,12 +240,13 @@ func (oc *OidcController) EndSessionHandler(c *gin.Context) {
|
|||||||
var input dto.OidcLogoutDto
|
var input dto.OidcLogoutDto
|
||||||
|
|
||||||
// Bind query parameters to the struct
|
// Bind query parameters to the struct
|
||||||
if c.Request.Method == http.MethodGet {
|
switch c.Request.Method {
|
||||||
|
case http.MethodGet:
|
||||||
if err := c.ShouldBindQuery(&input); err != nil {
|
if err := c.ShouldBindQuery(&input); err != nil {
|
||||||
_ = c.Error(err)
|
_ = c.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if c.Request.Method == http.MethodPost {
|
case http.MethodPost:
|
||||||
// Bind form parameters to the struct
|
// Bind form parameters to the struct
|
||||||
if err := c.ShouldBind(&input); err != nil {
|
if err := c.ShouldBind(&input); err != nil {
|
||||||
_ = c.Error(err)
|
_ = c.Error(err)
|
||||||
|
|||||||
@@ -63,13 +63,14 @@ func mapStructInternal(sourceVal reflect.Value, destVal reflect.Value) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mapField(sourceField reflect.Value, destField reflect.Value) error {
|
func mapField(sourceField reflect.Value, destField reflect.Value) error {
|
||||||
if sourceField.Type() == destField.Type() {
|
switch {
|
||||||
|
case sourceField.Type() == destField.Type():
|
||||||
destField.Set(sourceField)
|
destField.Set(sourceField)
|
||||||
} else if sourceField.Kind() == reflect.Slice && destField.Kind() == reflect.Slice {
|
case sourceField.Kind() == reflect.Slice && destField.Kind() == reflect.Slice:
|
||||||
return mapSlice(sourceField, destField)
|
return mapSlice(sourceField, destField)
|
||||||
} else if sourceField.Kind() == reflect.Struct && destField.Kind() == reflect.Struct {
|
case sourceField.Kind() == reflect.Struct && destField.Kind() == reflect.Struct:
|
||||||
return mapStructInternal(sourceField, destField)
|
return mapStructInternal(sourceField, destField)
|
||||||
} else {
|
default:
|
||||||
return mapSpecialTypes(sourceField, destField)
|
return mapSpecialTypes(sourceField, destField)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -98,8 +99,7 @@ func mapSlice(sourceField reflect.Value, destField reflect.Value) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mapSpecialTypes(sourceField reflect.Value, destField reflect.Value) error {
|
func mapSpecialTypes(sourceField reflect.Value, destField reflect.Value) error {
|
||||||
switch sourceField.Interface().(type) {
|
if _, ok := sourceField.Interface().(datatype.DateTime); ok {
|
||||||
case datatype.DateTime:
|
|
||||||
if sourceField.Type() == reflect.TypeOf(datatype.DateTime{}) && destField.Type() == reflect.TypeOf(time.Time{}) {
|
if sourceField.Type() == reflect.TypeOf(datatype.DateTime{}) && destField.Type() == reflect.TypeOf(time.Time{}) {
|
||||||
dateValue := sourceField.Interface().(datatype.DateTime)
|
dateValue := sourceField.Interface().(datatype.DateTime)
|
||||||
destField.Set(reflect.ValueOf(dateValue.ToTime()))
|
destField.Set(reflect.ValueOf(dateValue.ToTime()))
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package dto
|
package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin/binding"
|
|
||||||
"github.com/go-playground/validator/v10"
|
|
||||||
"log"
|
"log"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
)
|
)
|
||||||
|
|
||||||
var validateUsername validator.Func = func(fl validator.FieldLevel) bool {
|
var validateUsername validator.Func = func(fl validator.FieldLevel) bool {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/common"
|
"github.com/pocket-id/pocket-id/backend/internal/common"
|
||||||
)
|
)
|
||||||
@@ -23,7 +25,7 @@ func (m *CorsMiddleware) Add() gin.HandlerFunc {
|
|||||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "*")
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "*")
|
||||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
||||||
|
|
||||||
if c.Request.Method == "OPTIONS" {
|
if c.Request.Method == http.MethodOptions {
|
||||||
c.AbortWithStatus(204)
|
c.AbortWithStatus(204)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import datatype "github.com/pocket-id/pocket-id/backend/internal/model/types"
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/model/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ApiKey struct {
|
type ApiKey struct {
|
||||||
Base
|
Base
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/model/types"
|
datatype "github.com/pocket-id/pocket-id/backend/internal/model/types"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
datatype "github.com/pocket-id/pocket-id/backend/internal/model/types"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
datatype "github.com/pocket-id/pocket-id/backend/internal/model/types"
|
||||||
|
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/common"
|
"github.com/pocket-id/pocket-id/backend/internal/common"
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/dto"
|
"github.com/pocket-id/pocket-id/backend/internal/dto"
|
||||||
"github.com/pocket-id/pocket-id/backend/internal/model"
|
"github.com/pocket-id/pocket-id/backend/internal/model"
|
||||||
|
|||||||
@@ -105,9 +105,10 @@ func (s *CustomClaimService) updateCustomClaims(idType idType, value string, cla
|
|||||||
Value: claim.Value,
|
Value: claim.Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
if idType == UserID {
|
switch idType {
|
||||||
|
case UserID:
|
||||||
customClaim.UserID = &value
|
customClaim.UserID = &value
|
||||||
} else if idType == UserGroupID {
|
case UserGroupID:
|
||||||
customClaim.UserGroupID = &value
|
customClaim.UserGroupID = &value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ func TestGenerateVerifyAccessToken(t *testing.T) {
|
|||||||
assert.False(t, isAdmin, "isAdmin should be false")
|
assert.False(t, isAdmin, "isAdmin should be false")
|
||||||
audience, ok := claims.Audience()
|
audience, ok := claims.Audience()
|
||||||
_ = assert.True(t, ok, "Audience not found in token") &&
|
_ = assert.True(t, ok, "Audience not found in token") &&
|
||||||
assert.EqualValues(t, []string{"https://test.example.com"}, audience, "Audience should contain the app URL")
|
assert.Equal(t, []string{"https://test.example.com"}, audience, "Audience should contain the app URL")
|
||||||
|
|
||||||
// Check token expiration time is approximately 1 hour from now
|
// Check token expiration time is approximately 1 hour from now
|
||||||
expectedExp := time.Now().Add(1 * time.Hour)
|
expectedExp := time.Now().Add(1 * time.Hour)
|
||||||
@@ -606,7 +606,7 @@ func TestGenerateVerifyIdToken(t *testing.T) {
|
|||||||
assert.Equal(t, "user123", subject, "Token subject should match user ID")
|
assert.Equal(t, "user123", subject, "Token subject should match user ID")
|
||||||
audience, ok := claims.Audience()
|
audience, ok := claims.Audience()
|
||||||
_ = assert.True(t, ok, "Audience not found in token") &&
|
_ = assert.True(t, ok, "Audience not found in token") &&
|
||||||
assert.EqualValues(t, []string{clientID}, audience, "Audience should contain the client ID")
|
assert.Equal(t, []string{clientID}, audience, "Audience should contain the client ID")
|
||||||
issuer, ok := claims.Issuer()
|
issuer, ok := claims.Issuer()
|
||||||
_ = assert.True(t, ok, "Issuer not found in token") &&
|
_ = assert.True(t, ok, "Issuer not found in token") &&
|
||||||
assert.Equal(t, common.EnvConfig.AppURL, issuer, "Issuer should match app URL")
|
assert.Equal(t, common.EnvConfig.AppURL, issuer, "Issuer should match app URL")
|
||||||
@@ -935,7 +935,7 @@ func TestGenerateVerifyOauthAccessToken(t *testing.T) {
|
|||||||
assert.Equal(t, user.ID, subject, "Token subject should match user ID")
|
assert.Equal(t, user.ID, subject, "Token subject should match user ID")
|
||||||
audience, ok := claims.Audience()
|
audience, ok := claims.Audience()
|
||||||
_ = assert.True(t, ok, "Audience not found in token") &&
|
_ = assert.True(t, ok, "Audience not found in token") &&
|
||||||
assert.EqualValues(t, []string{clientID}, audience, "Audience should contain the client ID")
|
assert.Equal(t, []string{clientID}, audience, "Audience should contain the client ID")
|
||||||
issuer, ok := claims.Issuer()
|
issuer, ok := claims.Issuer()
|
||||||
_ = assert.True(t, ok, "Issuer not found in token") &&
|
_ = assert.True(t, ok, "Issuer not found in token") &&
|
||||||
assert.Equal(t, common.EnvConfig.AppURL, issuer, "Issuer should match app URL")
|
assert.Equal(t, common.EnvConfig.AppURL, issuer, "Issuer should match app URL")
|
||||||
@@ -1050,7 +1050,7 @@ func TestGenerateVerifyOauthAccessToken(t *testing.T) {
|
|||||||
assert.Equal(t, user.ID, subject, "Token subject should match user ID")
|
assert.Equal(t, user.ID, subject, "Token subject should match user ID")
|
||||||
audience, ok := claims.Audience()
|
audience, ok := claims.Audience()
|
||||||
_ = assert.True(t, ok, "Audience not found in token") &&
|
_ = assert.True(t, ok, "Audience not found in token") &&
|
||||||
assert.EqualValues(t, []string{clientID}, audience, "Audience should contain the client ID")
|
assert.Equal(t, []string{clientID}, audience, "Audience should contain the client ID")
|
||||||
|
|
||||||
// Verify the key type is OKP
|
// Verify the key type is OKP
|
||||||
publicKey, err := service.GetPublicJWK()
|
publicKey, err := service.GetPublicJWK()
|
||||||
@@ -1104,7 +1104,7 @@ func TestGenerateVerifyOauthAccessToken(t *testing.T) {
|
|||||||
assert.Equal(t, user.ID, subject, "Token subject should match user ID")
|
assert.Equal(t, user.ID, subject, "Token subject should match user ID")
|
||||||
audience, ok := claims.Audience()
|
audience, ok := claims.Audience()
|
||||||
_ = assert.True(t, ok, "Audience not found in token") &&
|
_ = assert.True(t, ok, "Audience not found in token") &&
|
||||||
assert.EqualValues(t, []string{clientID}, audience, "Audience should contain the client ID")
|
assert.Equal(t, []string{clientID}, audience, "Audience should contain the client ID")
|
||||||
|
|
||||||
// Verify the key type is EC
|
// Verify the key type is EC
|
||||||
publicKey, err := service.GetPublicJWK()
|
publicKey, err := service.GetPublicJWK()
|
||||||
@@ -1158,7 +1158,7 @@ func TestGenerateVerifyOauthAccessToken(t *testing.T) {
|
|||||||
assert.Equal(t, user.ID, subject, "Token subject should match user ID")
|
assert.Equal(t, user.ID, subject, "Token subject should match user ID")
|
||||||
audience, ok := claims.Audience()
|
audience, ok := claims.Audience()
|
||||||
_ = assert.True(t, ok, "Audience not found in token") &&
|
_ = assert.True(t, ok, "Audience not found in token") &&
|
||||||
assert.EqualValues(t, []string{clientID}, audience, "Audience should contain the client ID")
|
assert.Equal(t, []string{clientID}, audience, "Audience should contain the client ID")
|
||||||
|
|
||||||
// Verify the key type is RSA
|
// Verify the key type is RSA
|
||||||
publicKey, err := service.GetPublicJWK()
|
publicKey, err := service.GetPublicJWK()
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ func TestGetAuthenticatorName(t *testing.T) {
|
|||||||
|
|
||||||
// Inject a test AAGUID map
|
// Inject a test AAGUID map
|
||||||
aaguidMap = map[string]string{
|
aaguidMap = map[string]string{
|
||||||
"adce0002-35bc-c60a-648b-m0b25f1f05503": "Test Authenticator",
|
"adce0002-35bc-c60a-648b-0b25f1f05503": "Test Authenticator",
|
||||||
"00000000-0000-0000-0000-000000000000": "Zero Authenticator",
|
"00000000-0000-0000-0000-000000000000": "Zero Authenticator",
|
||||||
}
|
}
|
||||||
aaguidMapOnce = sync.Once{}
|
aaguidMapOnce = sync.Once{}
|
||||||
aaguidMapOnce.Do(func() {}) // Mark as done to avoid loading from file
|
aaguidMapOnce.Do(func() {}) // Mark as done to avoid loading from file
|
||||||
|
|||||||
@@ -171,14 +171,12 @@ func (c *Composer) String() string {
|
|||||||
func convertRunes(str string) []string {
|
func convertRunes(str string) []string {
|
||||||
var enc = make([]string, 0, len(str))
|
var enc = make([]string, 0, len(str))
|
||||||
for _, r := range str {
|
for _, r := range str {
|
||||||
if r == ' ' {
|
switch {
|
||||||
|
case r == ' ':
|
||||||
enc = append(enc, "_")
|
enc = append(enc, "_")
|
||||||
} else if isPrintableASCIIRune(r) &&
|
case isPrintableASCIIRune(r) && r != '=' && r != '?' && r != '_':
|
||||||
r != '=' &&
|
|
||||||
r != '?' &&
|
|
||||||
r != '_' {
|
|
||||||
enc = append(enc, string(r))
|
enc = append(enc, string(r))
|
||||||
} else {
|
default:
|
||||||
enc = append(enc, string(toHex([]byte(string(r)))))
|
enc = append(enc, string(toHex([]byte(string(r)))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user