代码质量优化

This commit is contained in:
ryan
2026-06-09 12:16:30 +08:00
parent f428839602
commit 4ac9857fe8
30 changed files with 199 additions and 149 deletions
+52
View File
@@ -0,0 +1,52 @@
version: "2"
run:
timeout: 5m
tests: false
linters:
default: none
enable:
# 基础检查
- govet
- staticcheck
- errcheck
- ineffassign
- unused
# 代码坏味道
- dupl # 重复代码
- mnd # 魔法数字
- gocyclo # 圈复杂度
- cyclop # 包/函数复杂度
- nestif # if 嵌套太深
- maintidx # 维护性指数
- revive # 风格/命名/坏味道
- gocritic # 各类代码问题
settings:
dupl:
threshold: 150
gocyclo:
min-complexity: 20
cyclop:
max-complexity: 15
package-average: 10
funlen:
lines: 150
statements: 80
nestif:
min-complexity: 5
mnd:
checks:
- argument
- case
- condition
- operation
- return
- assign
+12 -9
View File
@@ -473,21 +473,24 @@ export function FeatureMain() {
## 五、代码质量与审查规范
### 5.1 统一代码检查与提交
### 5.1 Make 指令
| 指令 | 触发时机 | 说明 |
|------|----------|------|
| `make code-check` | **提交前必须执行** | 前端 TypeScript 类型检查 + ESLint 静态分析;后端 `golangci-lint` 代码规范扫描 |
| `make build-test` | 功能完成后验证 | 前后端**并行**完整编译测试(`pnpm build` + `go build`),快速发现编译错误 |
| `make swagger` | 新增/修改 API 后 | 自动生成/更新 Swagger 文档(`docs/swagger.json`) |
| `make build-embedded` | 发布前 | 前端静态导出嵌入后端,生成单二进制产物 `bin/wavelet` |
| `make license` | 新增 Go 文件后 | 自动为所有 Go 源文件添加/更新 License Header |
| `make license-check` | CI 流水线 | 校验所有 Go 文件的 License Header 是否合规 |
> [!IMPORTANT]
> **代码开发完成后,提交前必须在项目根目录运行 `make code-check` 进行本地代码质量与风格检查,确保前端 ESLint 没有报错和警告(`--max-warnings 0`)。**
> **代码开发完成后,提交前必须运行 `make code-check`,所有检查全部通过后方可提交。**
```bash
# 执行根目录的检查指令
make code-check
```
---
### 5.2 后端规范检查
**基础检查**:
- 后端 Go 代码需要通过 CodeQL 扫描。较复杂的逻辑建议结合 Copilot 检查。
**API 文档**:
- 所有 HTTP 接口都必须编写完整的 Swagger 注释。提交前需运行 `make swagger` 自动生成与更新接口文档。
+19 -2
View File
@@ -1,4 +1,4 @@
.PHONY: swagger license license-check build-embedded
.PHONY: swagger license license-check build-embedded build-test
swagger:
scripts/swagger.sh
@@ -16,4 +16,21 @@ build-embedded:
go build -tags embed_frontend -o bin/wavelet main.go
code-check:
cd frontend && npx eslint . --max-warnings 0
cd frontend && pnpm tsc --noEmit --jsx preserve && npx eslint . --max-warnings 0
golangci-lint run
build-test:
@echo "==> Running frontend and backend build tests in parallel..."
@PIDS=""; \
STATUS=0; \
( cd frontend && pnpm build 2>&1 | sed 's/^/[frontend] /' ) & PIDS="$$PIDS $$!"; \
( go build -o /dev/null ./... 2>&1 | sed 's/^/[backend] /' ) & PIDS="$$PIDS $$!"; \
for PID in $$PIDS; do \
wait $$PID || STATUS=1; \
done; \
if [ $$STATUS -eq 0 ]; then \
echo "==> All build tests passed."; \
else \
echo "==> Build test FAILED." >&2; \
exit 1; \
fi
@@ -45,7 +45,7 @@ import {
} from "@/components/ui/dialog"
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from "@/components/ui/select"
import {Label} from "@/components/ui/label"
import {AuthService} from "@/lib/services"
import {AuthService, ChangePasswordRequest, UpdateProfileRequest} from "@/lib/services"
import {UploadService} from "@/lib/services/upload/upload.service"
import {ImageCrop, ImageCropApply, ImageCropContent, ImageCropReset} from "@/components/ui/image-crop"
import {toast} from "sonner"
@@ -92,7 +92,7 @@ export function ProfileMain() {
}, [user, isEditDialogOpen])
const changePasswordMutation = useMutation({
mutationFn: AuthService.changePassword,
mutationFn: (req: ChangePasswordRequest) => AuthService.changePassword(req),
onSuccess: () => {
toast.success("密码修改成功")
setOldPassword("")
@@ -122,7 +122,7 @@ export function ProfileMain() {
}
const updateProfileMutation = useMutation({
mutationFn: AuthService.updateProfile,
mutationFn: (req: UpdateProfileRequest) => AuthService.updateProfile(req),
onSuccess: () => {
toast.success("个人信息修改成功")
setIsEditDialogOpen(false)
@@ -81,11 +81,11 @@ func TestListAuthSources(t *testing.T) {
}
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
dataBytes, _ := json.Marshal(resp.Data)
var sources []model.AuthSource
json.Unmarshal(dataBytes, &sources)
_ = json.Unmarshal(dataBytes, &sources)
if len(sources) != 1 {
t.Errorf("expected 1 auth source, got %d", len(sources))
+5 -5
View File
@@ -103,7 +103,7 @@ func HandleLogWebSocket(c *gin.Context) {
if err != nil {
return
}
defer conn.Close()
defer func() { _ = conn.Close() }()
// 订阅 ring buffer
ch := logger.GlobalRingBuffer.Subscribe()
@@ -296,7 +296,7 @@ func GetAccessLogs(c *gin.Context) {
c.JSON(http.StatusInternalServerError, util.Err("查询 ClickHouse 日志明细失败: "+err.Error()))
return
}
defer rows.Close()
defer func() { _ = rows.Close() }()
var list []accessLogItem
var fetchUserIDs []uint64
@@ -424,7 +424,7 @@ func GetLogsAnalytics(c *gin.Context) {
}
if err == nil {
defer trendRows.Close()
defer func() { _ = trendRows.Close() }()
for trendRows.Next() {
var dt time.Time
var cnt uint64
@@ -454,7 +454,7 @@ func GetLogsAnalytics(c *gin.Context) {
browserCounts := make(map[string]uint64)
if err == nil {
defer uaRows.Close()
defer func() { _ = uaRows.Close() }()
for uaRows.Next() {
var ua string
var cnt uint64
@@ -493,7 +493,7 @@ func GetLogsAnalytics(c *gin.Context) {
userCountMap := make(map[uint64]uint64)
if err == nil {
defer userRows.Close()
defer func() { _ = userRows.Close() }()
for userRows.Next() {
var uid uint64
var cnt uint64
+1 -1
View File
@@ -206,7 +206,7 @@ func UpdateSystemConfig(c *gin.Context) {
updates := map[string]interface{}{
"description": req.Description,
}
if !(key == model.ConfigKeySMTPPassword && req.Value == "******") {
if key != model.ConfigKeySMTPPassword || req.Value != "******" {
updates["value"] = req.Value
config.Value = req.Value
}
@@ -140,11 +140,11 @@ func TestListSystemConfigs(t *testing.T) {
}
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
dataBytes, _ := json.Marshal(resp.Data)
var configs []model.SystemConfig
json.Unmarshal(dataBytes, &configs)
_ = json.Unmarshal(dataBytes, &configs)
// Defaults seed 23 configurations
if len(configs) != 23 {
@@ -158,11 +158,11 @@ func TestListSystemConfigs(t *testing.T) {
router.ServeHTTP(w, req)
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
dataBytes, _ := json.Marshal(resp.Data)
var configs []model.SystemConfig
json.Unmarshal(dataBytes, &configs)
_ = json.Unmarshal(dataBytes, &configs)
if len(configs) != 1 || configs[0].Key != model.ConfigKeyMaxAPIKeysPerUser {
t.Errorf("expected 1 business config (max_api_keys_per_user), got %d: %v", len(configs), configs)
@@ -187,11 +187,11 @@ func TestGetSystemConfig(t *testing.T) {
}
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
dataBytes, _ := json.Marshal(resp.Data)
var cfg model.SystemConfig
json.Unmarshal(dataBytes, &cfg)
_ = json.Unmarshal(dataBytes, &cfg)
if cfg.Value != "Wavelet" {
t.Errorf("expected 'Wavelet', got '%s'", cfg.Value)
@@ -240,7 +240,7 @@ func TestUpdateSystemConfig(t *testing.T) {
// Verify Redis
var redisConfig model.SystemConfig
db.HGetJSON(context.Background(), model.SystemConfigRedisHashKey, model.ConfigKeySiteName, &redisConfig)
_ = db.HGetJSON(context.Background(), model.SystemConfigRedisHashKey, model.ConfigKeySiteName, &redisConfig)
if redisConfig.Value != "Super Site Name" {
t.Errorf("redis cache value not updated, got '%s'", redisConfig.Value)
}
@@ -275,7 +275,7 @@ func TestTestSMTP(t *testing.T) {
if err != nil {
t.Fatalf("failed to start mock smtp server: %v", err)
}
defer l.Close()
defer func() { _ = l.Close() }()
port := l.Addr().(*net.TCPAddr).Port
@@ -284,40 +284,40 @@ func TestTestSMTP(t *testing.T) {
if err != nil {
return
}
defer conn.Close()
defer func() { _ = conn.Close() }()
writer := bufio.NewWriter(conn)
reader := bufio.NewReader(conn)
tp := textproto.NewReader(reader)
// 220 Ready
writer.WriteString("220 mock.smtp.com SMTP Ready\r\n")
writer.Flush()
_, _ = writer.WriteString("220 mock.smtp.com SMTP Ready\r\n")
_ = writer.Flush()
// Read HELO/EHLO
tp.ReadLine()
writer.WriteString("250-mock.smtp.com\r\n250 AUTH PLAIN\r\n")
writer.Flush()
_, _ = tp.ReadLine()
_, _ = writer.WriteString("250-mock.smtp.com\r\n250 AUTH PLAIN\r\n")
_ = writer.Flush()
// Read AUTH PLAIN
tp.ReadLine()
writer.WriteString("235 Authentication successful\r\n")
writer.Flush()
_, _ = tp.ReadLine()
_, _ = writer.WriteString("235 Authentication successful\r\n")
_ = writer.Flush()
// Read MAIL FROM
tp.ReadLine()
writer.WriteString("250 OK\r\n")
writer.Flush()
_, _ = tp.ReadLine()
_, _ = writer.WriteString("250 OK\r\n")
_ = writer.Flush()
// Read RCPT TO
tp.ReadLine()
writer.WriteString("250 OK\r\n")
writer.Flush()
_, _ = tp.ReadLine()
_, _ = writer.WriteString("250 OK\r\n")
_ = writer.Flush()
// Read DATA
tp.ReadLine()
writer.WriteString("354 Start mail input\r\n")
writer.Flush()
_, _ = tp.ReadLine()
_, _ = writer.WriteString("354 Start mail input\r\n")
_ = writer.Flush()
// Read body lines until dot
for {
@@ -326,13 +326,13 @@ func TestTestSMTP(t *testing.T) {
break
}
}
writer.WriteString("250 OK\r\n")
writer.Flush()
_, _ = writer.WriteString("250 OK\r\n")
_ = writer.Flush()
// Read QUIT
tp.ReadLine()
writer.WriteString("221 Bye\r\n")
writer.Flush()
_, _ = tp.ReadLine()
_, _ = writer.WriteString("221 Bye\r\n")
_ = writer.Flush()
}()
payload := TestSMTPRequest{
+4 -4
View File
@@ -45,7 +45,7 @@ func setupTaskTestEnvironment(t *testing.T) func() {
})
return func() {
if task.AsynqClient != nil {
task.AsynqClient.Close()
_ = task.AsynqClient.Close()
task.AsynqClient = nil
}
cleanup()
@@ -89,11 +89,11 @@ func TestListTaskTypes(t *testing.T) {
}
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
dataBytes, _ := json.Marshal(resp.Data)
var taskMetas []task.TaskMeta
json.Unmarshal(dataBytes, &taskMetas)
_ = json.Unmarshal(dataBytes, &taskMetas)
if len(taskMetas) == 0 {
t.Error("expected at least one dispatchable task type")
@@ -131,7 +131,7 @@ func TestDispatchTask(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code, "Body: %s", w.Body.String())
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
assert.Empty(t, resp.ErrorMsg)
assert.NotNil(t, resp.Data)
+2 -2
View File
@@ -136,11 +136,11 @@ func TestListTemplates(t *testing.T) {
}
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
dataBytes, _ := json.Marshal(resp.Data)
var templates []model.Template
json.Unmarshal(dataBytes, &templates)
_ = json.Unmarshal(dataBytes, &templates)
if len(templates) != 2 {
t.Errorf("expected 2 templates, got %d", len(templates))
+6 -6
View File
@@ -131,11 +131,11 @@ func TestListUsers(t *testing.T) {
router.ServeHTTP(w, req)
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
dataBytes, _ := json.Marshal(resp.Data)
var listResp listUsersResponse
json.Unmarshal(dataBytes, &listResp)
_ = json.Unmarshal(dataBytes, &listResp)
if len(listResp.Users) != 1 || listResp.Users[0].ID != 1001 {
t.Errorf("expected 1 user with ID 1001, got total %d", len(listResp.Users))
@@ -148,11 +148,11 @@ func TestListUsers(t *testing.T) {
router.ServeHTTP(w, req)
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
dataBytes, _ := json.Marshal(resp.Data)
var listResp listUsersResponse
json.Unmarshal(dataBytes, &listResp)
_ = json.Unmarshal(dataBytes, &listResp)
if len(listResp.Users) != 1 || listResp.Users[0].Username != "bob" {
t.Errorf("expected bob, got %v", listResp.Users)
@@ -226,7 +226,7 @@ func TestUpdateUserStatus(t *testing.T) {
}
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
if resp.ErrorMsg != cannotDisable {
t.Errorf("expected error message '%s', got '%s'", cannotDisable, resp.ErrorMsg)
}
@@ -326,7 +326,7 @@ func TestCreateUser(t *testing.T) {
}
var resp util.ResponseAny
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
if resp.ErrorMsg != usernameExists {
t.Errorf("expected error '%s', got '%s'", usernameExists, resp.ErrorMsg)
}
+3 -12
View File
@@ -214,15 +214,6 @@ func oidcDiscoveryResponse() *http.Response {
}
}
func jwksResponse() *http.Response {
jwksJSON, _ := json.Marshal(testJWKS)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader(jwksJSON)),
Header: make(http.Header),
}
}
type mockClaims struct {
ID uint64 `json:"id"`
Issuer string `json:"iss"`
@@ -822,7 +813,7 @@ func TestCallbackBind(t *testing.T) {
router.GET("/test-helper/login-777", func(c *gin.Context) {
session := sessions.Default(c)
session.Set(UserIDKey, uint64(777))
session.Save()
_ = session.Save()
c.String(200, "ok")
})
@@ -874,7 +865,7 @@ func TestCallbackBind(t *testing.T) {
router.GET("/test-helper/login-888", func(c *gin.Context) {
session := sessions.Default(c)
session.Set(UserIDKey, uint64(888))
session.Save()
_ = session.Save()
c.String(200, "ok")
})
@@ -937,7 +928,7 @@ func TestExternalAccountsListAndDelete(t *testing.T) {
router.GET("/test-helper/login-555", func(c *gin.Context) {
session := sessions.Default(c)
session.Set(UserIDKey, uint64(555))
session.Save()
_ = session.Save()
c.String(200, "ok")
})
+1 -1
View File
@@ -82,7 +82,7 @@ func ServeFileByID(c *gin.Context) {
}
// Stream from CDN/S3
defer obj.Body.Close()
defer func() { _ = obj.Body.Close() }()
// Respond with the file content
c.DataFromReader(http.StatusOK, obj.ContentLength, obj.ContentType, obj.Body, nil)
+3 -3
View File
@@ -88,7 +88,7 @@ func UploadFile(c *gin.Context) {
c.JSON(http.StatusOK, util.Err(ErrOpenFileFailed))
return
}
defer file.Close()
defer func() { _ = file.Close() }()
// 校验大小
if header.Size > maxUploadSize {
@@ -318,7 +318,7 @@ func DownloadFile(c *gin.Context) {
return
}
defer obj.Body.Close()
defer func() { _ = obj.Body.Close() }()
_, _ = io.Copy(c.Writer, obj.Body)
}
@@ -372,7 +372,7 @@ func BatchDownloadFiles(c *gin.Context) {
// 开启实时 ZIP 压缩器并直接输出给 Response Writer
zipWriter := zip.NewWriter(c.Writer)
defer zipWriter.Close()
defer func() { _ = zipWriter.Close() }()
// 用于解决 ZIP 内部文件名称发生碰撞冲突的问题
usedNames := make(map[string]int)
+7 -7
View File
@@ -95,7 +95,7 @@ func createMultipartRequest(t *testing.T, fieldName, fileName string, fileConten
func TestUploadFile(t *testing.T) {
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
defer cleanup()
defer os.RemoveAll("uploads") // Clean up local files created during tests
defer func() { _ = os.RemoveAll("uploads") }() // Clean up local files created during tests
authUser := &model.User{ID: 1001, Username: "test_user"}
router := setupTestRouter(authUser)
@@ -211,7 +211,7 @@ func TestUploadFile(t *testing.T) {
}
var resp testResponse
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
if resp.ErrorMsg == "" || !strings.Contains(resp.ErrorMsg, ErrUnsupportedFormat) {
t.Errorf("expected unsupported format error, got: %v", resp)
}
@@ -247,7 +247,7 @@ func TestUploadFile(t *testing.T) {
}
var resp2 testResponse
json.Unmarshal(w2.Body.Bytes(), &resp2)
_ = json.Unmarshal(w2.Body.Bytes(), &resp2)
if resp2.ErrorMsg != "" {
t.Fatalf("second upload was unsuccessful: %s", resp2.ErrorMsg)
@@ -304,7 +304,7 @@ func TestUploadFile(t *testing.T) {
}
var resp testResponse
json.Unmarshal(w.Body.Bytes(), &resp)
_ = json.Unmarshal(w.Body.Bytes(), &resp)
if resp.ErrorMsg != "" {
t.Fatalf("local upload failed: %s", resp.ErrorMsg)
@@ -334,7 +334,7 @@ func TestUploadFile(t *testing.T) {
func TestDownloadFile(t *testing.T) {
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
defer cleanup()
defer os.RemoveAll("uploads")
defer func() { _ = os.RemoveAll("uploads") }()
authUser := &model.User{ID: 1001, Username: "test_user"}
router := setupTestRouter(authUser)
@@ -403,7 +403,7 @@ func TestDownloadFile(t *testing.T) {
func TestBatchDownloadFiles(t *testing.T) {
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
defer cleanup()
defer os.RemoveAll("uploads")
defer func() { _ = os.RemoveAll("uploads") }()
authUser := &model.User{ID: 1001, Username: "test_user"}
router := setupTestRouter(authUser)
@@ -495,7 +495,7 @@ func TestBatchDownloadFiles(t *testing.T) {
t.Fatalf("failed to open zip file entry %s: %v", f.Name, err)
}
content, _ := io.ReadAll(rc)
rc.Close()
_ = rc.Close()
extracted[f.Name] = string(content)
}
+4 -3
View File
@@ -18,6 +18,7 @@ limitations under the License.
package upload
import (
"errors"
"fmt"
"strings"
)
@@ -27,7 +28,7 @@ const maxS3KeyLength = 1024
// ValidateS3Key validates an S3 object key for safety.
func ValidateS3Key(key string) error {
if key == "" {
return fmt.Errorf(ErrS3KeyRequired)
return errors.New(ErrS3KeyRequired)
}
if len(key) > maxS3KeyLength {
@@ -35,11 +36,11 @@ func ValidateS3Key(key string) error {
}
if strings.HasPrefix(key, "/") {
return fmt.Errorf(ErrS3KeyStartsWithSlash)
return errors.New(ErrS3KeyStartsWithSlash)
}
if strings.Contains(key, "\x00") {
return fmt.Errorf(ErrS3KeyContainsNullBytes)
return errors.New(ErrS3KeyContainsNullBytes)
}
return nil
+2 -2
View File
@@ -125,7 +125,7 @@ func sendEmailVerificationCode(ctx context.Context, email, scene, templateName s
// 存验证码,5分钟有效
if err := db.SetJSON(ctx, codeKey, code, 5*time.Minute); err != nil {
return fmt.Errorf(errGenerateEmailCodeFailed)
return errors.New(errGenerateEmailCodeFailed)
}
// 存冷却,60秒有效
_ = db.SetJSON(ctx, cooldownKey, "1", 60*time.Second)
@@ -139,7 +139,7 @@ func sendEmailVerificationCode(ctx context.Context, email, scene, templateName s
payloadBytes, _ := json.Marshal(payload)
_, err = task.DispatchTask(ctx, task.TaskTypeSendEmail, payloadBytes, "system")
if err != nil {
return fmt.Errorf(errDispatchEmailTaskFailed)
return errors.New(errDispatchEmailTaskFailed)
}
return nil
}
+11 -11
View File
@@ -27,7 +27,7 @@ func TestLogRingBuffer_WriteAndQuery(t *testing.T) {
rb := NewLogRingBuffer(5)
// Write some logs
rb.Write([]byte("line1\nline2\nline3\n"))
_, _ = rb.Write([]byte("line1\nline2\nline3\n"))
entries, hasMore := rb.Query(0, 10)
assert.False(t, hasMore)
@@ -43,7 +43,7 @@ func TestLogRingBuffer_WriteAndQuery(t *testing.T) {
func TestLogRingBuffer_CapacityOverflow(t *testing.T) {
rb := NewLogRingBuffer(3)
rb.Write([]byte("a\nb\nc\nd\ne\n"))
_, _ = rb.Write([]byte("a\nb\nc\nd\ne\n"))
entries, hasMore := rb.Query(0, 10)
assert.False(t, hasMore)
@@ -56,7 +56,7 @@ func TestLogRingBuffer_CapacityOverflow(t *testing.T) {
func TestLogRingBuffer_QueryLatest(t *testing.T) {
rb := NewLogRingBuffer(10)
rb.Write([]byte("a\nb\nc\nd\ne\n"))
_, _ = rb.Write([]byte("a\nb\nc\nd\ne\n"))
// Query latest 2
entries, hasMore := rb.Query(0, 2)
@@ -69,7 +69,7 @@ func TestLogRingBuffer_QueryLatest(t *testing.T) {
func TestLogRingBuffer_QueryByCursor(t *testing.T) {
rb := NewLogRingBuffer(10)
rb.Write([]byte("a\nb\nc\nd\ne\n"))
_, _ = rb.Write([]byte("a\nb\nc\nd\ne\n"))
// First get all to find indices
all, _ := rb.Query(0, 10)
@@ -87,7 +87,7 @@ func TestLogRingBuffer_QueryByCursor(t *testing.T) {
func TestLogRingBuffer_QueryByCursorWithLimit(t *testing.T) {
rb := NewLogRingBuffer(10)
rb.Write([]byte("a\nb\nc\nd\ne\n"))
_, _ = rb.Write([]byte("a\nb\nc\nd\ne\n"))
// Query 2 entries before index 4
entries, hasMore := rb.Query(4, 2)
@@ -107,7 +107,7 @@ func TestLogRingBuffer_QueryEmpty(t *testing.T) {
func TestLogRingBuffer_QueryNonExistentCursor(t *testing.T) {
rb := NewLogRingBuffer(5)
rb.Write([]byte("a\nb\n"))
_, _ = rb.Write([]byte("a\nb\n"))
entries, hasMore := rb.Query(999, 10)
assert.False(t, hasMore)
@@ -122,7 +122,7 @@ func TestLogRingBuffer_Subscribe(t *testing.T) {
ch := rb.Subscribe()
defer rb.Unsubscribe(ch)
rb.Write([]byte("hello\n"))
_, _ = rb.Write([]byte("hello\n"))
entry := <-ch
assert.Equal(t, "hello", entry.Data)
@@ -137,7 +137,7 @@ func TestLogRingBuffer_SubscribeMultiple(t *testing.T) {
ch2 := rb.Subscribe()
defer rb.Unsubscribe(ch2)
rb.Write([]byte("msg\n"))
_, _ = rb.Write([]byte("msg\n"))
e1 := <-ch1
e2 := <-ch2
@@ -148,7 +148,7 @@ func TestLogRingBuffer_SubscribeMultiple(t *testing.T) {
func TestLogRingBuffer_WriteNoNewline(t *testing.T) {
rb := NewLogRingBuffer(5)
rb.Write([]byte("partial"))
_, _ = rb.Write([]byte("partial"))
entries, _ := rb.Query(0, 10)
assert.Equal(t, 1, len(entries))
@@ -169,7 +169,7 @@ func TestLogRingBuffer_WriteEmpty(t *testing.T) {
func TestLogRingBuffer_QueryAfterOverflow(t *testing.T) {
rb := NewLogRingBuffer(3)
rb.Write([]byte("1\n2\n3\n4\n5\n6\n7\n"))
_, _ = rb.Write([]byte("1\n2\n3\n4\n5\n6\n7\n"))
entries, hasMore := rb.Query(0, 10)
assert.False(t, hasMore)
@@ -187,7 +187,7 @@ func TestLogRingBuffer_QueryAfterOverflow(t *testing.T) {
func TestLogRingBuffer_NextCursor(t *testing.T) {
rb := NewLogRingBuffer(10)
rb.Write([]byte("a\nb\nc\nd\ne\n"))
_, _ = rb.Write([]byte("a\nb\nc\nd\ne\n"))
// Query latest 2, should return next_cursor pointing to first returned entry
entries, _ := rb.Query(0, 2)
+1 -1
View File
@@ -277,7 +277,7 @@ func TestListTaskExecutions(t *testing.T) {
assert.Equal(t, "list_002", items[0].TaskID)
// 按类型筛选
items, total, err = ListTaskExecutions(ctx, ListTaskExecutionsRequest{TaskType: "other:task", Page: 1, PageSize: 10})
_, total, err = ListTaskExecutions(ctx, ListTaskExecutionsRequest{TaskType: "other:task", Page: 1, PageSize: 10})
require.NoError(t, err)
assert.Equal(t, int64(2), total)
+6 -4
View File
@@ -106,12 +106,14 @@ func GetObjectViaCache(ctx context.Context, key string) (*ObjectInfo, error) {
}
func getLocalCacheFile(ctx context.Context, localPath, metaPath string) (*ObjectInfo, error) {
ctx, span := otel_trace.Start(ctx, "S3.GetLocalCacheFile", trace.WithSpanKind(trace.SpanKindClient))
_, span := otel_trace.Start(ctx, "S3.GetLocalCacheFile", trace.WithSpanKind(trace.SpanKindClient))
defer span.End()
// 尝试打开本地缓存文件
file, err := os.Open(localPath)
defer file.Close()
if err == nil {
defer func() { _ = file.Close() }()
}
// 文件不存在
if err != nil && os.IsNotExist(err) {
@@ -149,7 +151,7 @@ func getLocalCacheFile(ctx context.Context, localPath, metaPath string) (*Object
}
func saveToLocalCache(ctx context.Context, localPath, metaPath string, objInfo *ObjectInfo) error {
ctx, span := otel_trace.Start(ctx, "S3.SaveToLocalCache", trace.WithSpanKind(trace.SpanKindClient))
_, span := otel_trace.Start(ctx, "S3.SaveToLocalCache", trace.WithSpanKind(trace.SpanKindClient))
defer span.End()
// 创建目录
@@ -186,7 +188,7 @@ func saveFile(localPath string, data io.Reader) error {
if err != nil {
return err
}
defer os.Remove(tempFile.Name())
defer func() { _ = os.Remove(tempFile.Name()) }()
// 将内容写入临时文件
if _, err := tempFile.ReadFrom(data); err != nil {
+1 -1
View File
@@ -227,7 +227,7 @@ func GetObjectViaProxy(ctx context.Context, key string) (*ObjectInfo, error) {
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
_ = resp.Body.Close()
span.SetStatus(codes.Error, fmt.Sprintf("cdn returned status %d", resp.StatusCode))
return nil, fmt.Errorf(errCDNStatusFailed, resp.StatusCode)
}
+2 -1
View File
@@ -19,6 +19,7 @@ package task
import (
"context"
"errors"
"fmt"
"time"
@@ -155,7 +156,7 @@ func RetryTask(ctx context.Context, id uint64) (string, error) {
}
if !execution.Retryable {
return "", fmt.Errorf(errTaskNotRetryable)
return "", errors.New(errTaskNotRetryable)
}
if execution.RetryCount >= execution.MaxRetry {
+1 -1
View File
@@ -73,7 +73,7 @@ func setupTest(t *testing.T) func() {
RegisterHandler(testTaskType, successHandler())
return func() {
if AsynqClient != nil {
AsynqClient.Close()
_ = AsynqClient.Close()
AsynqClient = nil
}
cleanup()
+1 -1
View File
@@ -74,7 +74,7 @@ func SetupTestEnvironment(t *testing.T) (*gorm.DB, *miniredis.Miniredis, func())
// Cleanup function
cleanup := func() {
redisClient.Close()
_ = redisClient.Close()
mr.Close()
// Reset database and Redis references
db.SetDB(nil)
+4 -4
View File
@@ -36,7 +36,7 @@ type ChallengeConfig struct {
Count int // Number of puzzles (c)
Size int // Salt length (s)
Difficulty int // Difficulty prefix length (d)
ExpiresMs time.Duration // Challenge TTL
Expires time.Duration // Challenge TTL
}
// ChallengeResponse is returned to the client
@@ -156,12 +156,12 @@ func GenerateChallenge(secret []byte, conf ChallengeConfig, scope string) (*Chal
if conf.Difficulty <= 0 {
conf.Difficulty = 4
}
if conf.ExpiresMs <= 0 {
conf.ExpiresMs = 10 * time.Minute
if conf.Expires <= 0 {
conf.Expires = 10 * time.Minute
}
now := time.Now().UnixNano() / int64(time.Millisecond)
expires := now + int64(conf.ExpiresMs/time.Millisecond)
expires := now + int64(conf.Expires/time.Millisecond)
payload := ChallengePayload{
Nonce: randomHex(25),
+1 -9
View File
@@ -75,7 +75,7 @@ func (m *Manager) Generate(ctx context.Context, scope string) (*ChallengeRespons
Count: m.getChallengeCount(ctx),
Size: m.getChallengeSize(ctx),
Difficulty: m.getChallengeDifficulty(ctx),
ExpiresMs: m.getChallengeTTL(ctx),
Expires: m.getChallengeTTL(ctx),
}
return GenerateChallenge(m.conf.Secret, c, scope)
}
@@ -190,14 +190,6 @@ func (m *Manager) VerifyToken(ctx context.Context, token string, expectedScope s
return true, nil
}
// sGet safely calls store.Get, treating a nil store as a miss.
func sGet(ctx context.Context, store Store, key string) (string, bool, error) {
if store == nil {
return "", false, nil
}
return store.Get(ctx, key)
}
// sGetAndDelete safely calls store.GetAndDelete, treating a nil store as a miss.
func sGetAndDelete(ctx context.Context, store Store, key string) (string, bool, error) {
if store == nil {
-5
View File
@@ -41,11 +41,6 @@ func fnv1aResume(state uint32, str string) uint32 {
return h
}
// prng generates a hex string of specified length using a seed
func prng(seed string, length int) string {
return prngFromHash(fnv1a(seed), length)
}
// prngFromHash generates a hex string of specified length using an initial hash state
func prngFromHash(initialHash uint32, length int) string {
state := initialHash
+4 -8
View File
@@ -58,16 +58,12 @@ func Request(ctx context.Context, method, url string, body io.Reader, headers, c
return nil, fmt.Errorf(errCreateHTTPRequestFailed, err)
}
if cookies != nil {
for key, value := range cookies {
req.AddCookie(&http.Cookie{Name: key, Value: value})
}
for key, value := range cookies {
req.AddCookie(&http.Cookie{Name: key, Value: value})
}
if headers != nil {
for key, value := range headers {
req.Header.Set(key, value)
}
for key, value := range headers {
req.Header.Set(key, value)
}
resp, err := httpClient.Do(req)
+8 -8
View File
@@ -71,14 +71,14 @@ func SendMailHTML(cfg Config, to string, subject, body string) error {
if err != nil {
return fmt.Errorf(errDialTLSFailed, err)
}
defer conn.Close()
defer func() { _ = conn.Close() }()
_ = conn.SetDeadline(time.Now().Add(10 * time.Second))
client, err := smtp.NewClient(conn, cfg.Host)
if err != nil {
return fmt.Errorf(errSMTPClientCreationFailed, err)
}
defer client.Close()
defer func() { _ = client.Close() }()
if err = client.Auth(auth); err != nil {
return fmt.Errorf(errSMTPAuthFailed, err)
@@ -96,7 +96,7 @@ func SendMailHTML(cfg Config, to string, subject, body string) error {
if err != nil {
return fmt.Errorf(errSMTPDataCommandFailed, err)
}
defer w.Close()
defer func() { _ = w.Close() }()
_, err = w.Write([]byte(message))
if err != nil {
@@ -119,7 +119,7 @@ func SendMailHTML(cfg Config, to string, subject, body string) error {
func SendMailWithLog(cfg Config, to string, subject, body string) (string, error) {
var logBuf bytes.Buffer
logLine := func(dir string, format string, args ...interface{}) {
logBuf.WriteString(fmt.Sprintf("[%s] %s\n", dir, fmt.Sprintf(format, args...)))
fmt.Fprintf(&logBuf, "[%s] %s\n", dir, fmt.Sprintf(format, args...))
}
addr := net.JoinHostPort(cfg.Host, strconv.Itoa(cfg.Port))
@@ -141,7 +141,7 @@ func SendMailWithLog(cfg Config, to string, subject, body string) (string, error
logLine("Error", "Connection failed: %v", err)
return logBuf.String(), err
}
defer conn.Close()
defer func() { _ = conn.Close() }()
logLine("System", "Connected successfully.")
// Set a 10-second session deadline for read/write operations
@@ -152,7 +152,7 @@ func SendMailWithLog(cfg Config, to string, subject, body string) (string, error
logLine("Error", "SMTP client handshake failed: %v", err)
return logBuf.String(), err
}
defer client.Close()
defer func() { _ = client.Close() }()
// If not 465, support STARTTLS if available
if cfg.Port != 465 {
@@ -222,11 +222,11 @@ func SendMailWithLog(cfg Config, to string, subject, body string) (string, error
logLine("System", "Sending message body...")
if _, err = w.Write([]byte(message)); err != nil {
w.Close()
_ = w.Close()
logLine("Error", "Writing message body failed: %v", err)
return logBuf.String(), err
}
w.Close()
_ = w.Close()
logLine("S", "250 OK")
logLine("C", "QUIT")
+2 -2
View File
@@ -30,7 +30,7 @@ func TestSendMailMock(t *testing.T) {
if err != nil {
t.Fatalf("failed to start mock smtp server: %v", err)
}
defer l.Close()
defer func() { _ = l.Close() }()
port := l.Addr().(*net.TCPAddr).Port
@@ -39,7 +39,7 @@ func TestSendMailMock(t *testing.T) {
if err != nil {
return
}
defer conn.Close()
defer func() { _ = conn.Close() }()
writer := bufio.NewWriter(conn)
reader := bufio.NewReader(conn)