Compare commits
2 Commits
2f2653c76f
...
fd12838519
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd12838519 | ||
|
|
64e14ac30d |
@@ -2339,3 +2339,53 @@
|
|||||||
- 现在真正剩下的窄点已经进一步收缩为:
|
- 现在真正剩下的窄点已经进一步收缩为:
|
||||||
- `PacksRepo.Upsert` 的剩余更新分支
|
- `PacksRepo.Upsert` 的剩余更新分支
|
||||||
- `ProvidersRepo.Upsert` 的剩余更新分支
|
- `ProvidersRepo.Upsert` 的剩余更新分支
|
||||||
|
|
||||||
|
## 2026-05-30 已完成 ProvidersRepo / PacksRepo Upsert 的最后一轮定点补测
|
||||||
|
|
||||||
|
**目标**:把上一轮剩下的最后两个窄点收口,直接命中 `ProvidersRepo.Upsert` 与 `PacksRepo.Upsert` 的回退/错误分支,而不是继续增加普通 happy path
|
||||||
|
|
||||||
|
**本次新增测试点**:
|
||||||
|
|
||||||
|
- `ProvidersRepo.Upsert`
|
||||||
|
- `ExecContext` 返回错误
|
||||||
|
- `LastInsertId` 不可用时回退到 `GetByPackIDAndProviderID`
|
||||||
|
- 回退读取失败时返回 `sql.ErrNoRows`
|
||||||
|
- `PacksRepo.Upsert`
|
||||||
|
- `ExecContext` 返回错误
|
||||||
|
- `LastInsertId` 不可用时回退到 `GetByPackID`
|
||||||
|
- 回退读取失败时返回 `sql.ErrNoRows`
|
||||||
|
|
||||||
|
**实现方式**:
|
||||||
|
|
||||||
|
- 新增测试内 `execQuerierStub`
|
||||||
|
- 新增测试内 `resultStub`
|
||||||
|
- 通过包装真实 SQLite `QueryRowContext`,只替换 `ExecContext / LastInsertId` 行为
|
||||||
|
- 这样能稳定命中 repo 自身的 fallback 分支,而不需要改生产代码
|
||||||
|
|
||||||
|
**验证结果**:
|
||||||
|
|
||||||
|
- 定向测试:
|
||||||
|
- `go test ./internal/store/sqlite -count=1` => `ok`
|
||||||
|
- 定向覆盖率:
|
||||||
|
- `go test -coverprofile=/tmp/sqlite-final-upsert.cover ./internal/store/sqlite` => `78.1%`
|
||||||
|
- 热点函数回读:
|
||||||
|
- `ProvidersRepo.Upsert = 96.0%`
|
||||||
|
- `PacksRepo.Upsert = 95.5%`
|
||||||
|
- 全量门禁:
|
||||||
|
- `gofmt -l .` => clean
|
||||||
|
- `go vet ./...` => `ok`
|
||||||
|
- `go test -cover ./internal/...` => `ok`
|
||||||
|
- `go test ./tests/integration/... -count=1` => `ok`
|
||||||
|
- `bash ./scripts/test/verify_quality_gates.sh` => `PASS`
|
||||||
|
- 真实门禁已在 unrestricted 环境确认
|
||||||
|
|
||||||
|
**覆盖率变化**:
|
||||||
|
|
||||||
|
- `internal/store/sqlite`
|
||||||
|
- 本轮前:`77.5%`
|
||||||
|
- 本轮后:`78.1%`
|
||||||
|
|
||||||
|
**结论**:
|
||||||
|
|
||||||
|
- `ProvidersRepo.Upsert / PacksRepo.Upsert` 已不再是这轮质量治理的主要薄点
|
||||||
|
- 这一波按执行板列出的热点定点补测,到这里已经基本收口
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -159,6 +160,87 @@ func TestPacksRepoUpsertTrimsAndDefaultsManifest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPacksRepoUpsertFallsBackWhenLastInsertIDUnavailable(t *testing.T) {
|
||||||
|
store := openTestDB(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
db := store.SQLDB()
|
||||||
|
repo := newPacksRepo(execQuerierStub{
|
||||||
|
execFn: func(ctx context.Context, query string, args ...any) (sql.Result, error) {
|
||||||
|
if _, err := db.ExecContext(ctx, query, args...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resultStub{lastInsertErr: errors.New("last insert unavailable")}, nil
|
||||||
|
},
|
||||||
|
queryFn: db.QueryContext,
|
||||||
|
queryRowFn: db.QueryRowContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
id, err := repo.Upsert(ctx, Pack{
|
||||||
|
PackID: "fallback-pack",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Checksum: "fallback-checksum",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Upsert() error = %v", err)
|
||||||
|
}
|
||||||
|
if id <= 0 {
|
||||||
|
t.Fatalf("Upsert() id = %d, want positive", id)
|
||||||
|
}
|
||||||
|
got, err := store.Packs().GetByPackID(ctx, "fallback-pack")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetByPackID() error = %v", err)
|
||||||
|
}
|
||||||
|
if got.ID != id {
|
||||||
|
t.Fatalf("fallback id = %d, want persisted id %d", id, got.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacksRepoUpsertReturnsExecError(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
repo := newPacksRepo(execQuerierStub{
|
||||||
|
execFn: func(context.Context, string, ...any) (sql.Result, error) {
|
||||||
|
return nil, errors.New("exec boom")
|
||||||
|
},
|
||||||
|
queryFn: func(context.Context, string, ...any) (*sql.Rows, error) {
|
||||||
|
return nil, errors.New("unexpected query")
|
||||||
|
},
|
||||||
|
queryRowFn: func(context.Context, string, ...any) *sql.Row {
|
||||||
|
panic("unexpected QueryRowContext")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := repo.Upsert(ctx, Pack{
|
||||||
|
PackID: "pack-exec-error",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Checksum: "checksum",
|
||||||
|
})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "exec boom") {
|
||||||
|
t.Fatalf("Upsert() error = %v, want exec boom", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacksRepoUpsertReturnsFallbackReadError(t *testing.T) {
|
||||||
|
store := openTestDB(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
db := store.SQLDB()
|
||||||
|
repo := newPacksRepo(execQuerierStub{
|
||||||
|
execFn: func(context.Context, string, ...any) (sql.Result, error) {
|
||||||
|
return resultStub{lastInsertErr: errors.New("last insert unavailable")}, nil
|
||||||
|
},
|
||||||
|
queryFn: db.QueryContext,
|
||||||
|
queryRowFn: db.QueryRowContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := repo.Upsert(ctx, Pack{
|
||||||
|
PackID: "pack-missing-after-upsert",
|
||||||
|
Version: "1.0.0",
|
||||||
|
Checksum: "checksum",
|
||||||
|
})
|
||||||
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
t.Fatalf("Upsert() error = %v, want sql.ErrNoRows fallback read error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPacksRepoValidationErrors(t *testing.T) {
|
func TestPacksRepoValidationErrors(t *testing.T) {
|
||||||
store := openTestDB(t)
|
store := openTestDB(t)
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,44 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type resultStub struct {
|
||||||
|
lastInsertID int64
|
||||||
|
lastInsertErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r resultStub) LastInsertId() (int64, error) {
|
||||||
|
if r.lastInsertErr != nil {
|
||||||
|
return 0, r.lastInsertErr
|
||||||
|
}
|
||||||
|
return r.lastInsertID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r resultStub) RowsAffected() (int64, error) {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type execQuerierStub struct {
|
||||||
|
execFn func(context.Context, string, ...any) (sql.Result, error)
|
||||||
|
queryFn func(context.Context, string, ...any) (*sql.Rows, error)
|
||||||
|
queryRowFn func(context.Context, string, ...any) *sql.Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s execQuerierStub) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) {
|
||||||
|
return s.execFn(ctx, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s execQuerierStub) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) {
|
||||||
|
return s.queryFn(ctx, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s execQuerierStub) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row {
|
||||||
|
return s.queryRowFn(ctx, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func TestProvidersRepoCreateAndGet(t *testing.T) {
|
func TestProvidersRepoCreateAndGet(t *testing.T) {
|
||||||
store := openTestDB(t)
|
store := openTestDB(t)
|
||||||
|
|
||||||
@@ -229,6 +264,95 @@ func TestProvidersRepoUpsertTrimsAndDefaultsOptionalJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProvidersRepoUpsertFallsBackWhenLastInsertIDUnavailable(t *testing.T) {
|
||||||
|
store := openTestDB(t)
|
||||||
|
packID := createTestPack(t, store)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db := store.SQLDB()
|
||||||
|
repo := newProvidersRepo(execQuerierStub{
|
||||||
|
execFn: func(ctx context.Context, query string, args ...any) (sql.Result, error) {
|
||||||
|
if _, err := db.ExecContext(ctx, query, args...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resultStub{lastInsertErr: errors.New("last insert unavailable")}, nil
|
||||||
|
},
|
||||||
|
queryFn: db.QueryContext,
|
||||||
|
queryRowFn: db.QueryRowContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
id, err := repo.Upsert(ctx, Provider{
|
||||||
|
PackID: packID,
|
||||||
|
ProviderID: "fallback-provider",
|
||||||
|
DisplayName: "Fallback Provider",
|
||||||
|
BaseURL: "https://fallback.example.com/v1",
|
||||||
|
Platform: "openai",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Upsert() error = %v", err)
|
||||||
|
}
|
||||||
|
if id <= 0 {
|
||||||
|
t.Fatalf("Upsert() id = %d, want positive", id)
|
||||||
|
}
|
||||||
|
got, err := store.Providers().GetByPackIDAndProviderID(ctx, packID, "fallback-provider")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetByPackIDAndProviderID() error = %v", err)
|
||||||
|
}
|
||||||
|
if got.ID != id {
|
||||||
|
t.Fatalf("fallback id = %d, want persisted id %d", id, got.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvidersRepoUpsertReturnsExecError(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
repo := newProvidersRepo(execQuerierStub{
|
||||||
|
execFn: func(context.Context, string, ...any) (sql.Result, error) {
|
||||||
|
return nil, errors.New("exec boom")
|
||||||
|
},
|
||||||
|
queryFn: func(context.Context, string, ...any) (*sql.Rows, error) {
|
||||||
|
return nil, errors.New("unexpected query")
|
||||||
|
},
|
||||||
|
queryRowFn: func(context.Context, string, ...any) *sql.Row {
|
||||||
|
panic("unexpected QueryRowContext")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := repo.Upsert(ctx, Provider{
|
||||||
|
PackID: 1,
|
||||||
|
ProviderID: "provider-exec-error",
|
||||||
|
DisplayName: "Provider Exec Error",
|
||||||
|
BaseURL: "https://exec-error.example.com/v1",
|
||||||
|
Platform: "openai",
|
||||||
|
})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "exec boom") {
|
||||||
|
t.Fatalf("Upsert() error = %v, want exec boom", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvidersRepoUpsertReturnsFallbackReadError(t *testing.T) {
|
||||||
|
store := openTestDB(t)
|
||||||
|
ctx := context.Background()
|
||||||
|
db := store.SQLDB()
|
||||||
|
repo := newProvidersRepo(execQuerierStub{
|
||||||
|
execFn: func(context.Context, string, ...any) (sql.Result, error) {
|
||||||
|
return resultStub{lastInsertErr: errors.New("last insert unavailable")}, nil
|
||||||
|
},
|
||||||
|
queryFn: db.QueryContext,
|
||||||
|
queryRowFn: db.QueryRowContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := repo.Upsert(ctx, Provider{
|
||||||
|
PackID: createTestPack(t, store),
|
||||||
|
ProviderID: "provider-missing-after-upsert",
|
||||||
|
DisplayName: "Provider Missing After Upsert",
|
||||||
|
BaseURL: "https://missing.example.com/v1",
|
||||||
|
Platform: "openai",
|
||||||
|
})
|
||||||
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
t.Fatalf("Upsert() error = %v, want sql.ErrNoRows fallback read error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProvidersRepoValidationErrors(t *testing.T) {
|
func TestProvidersRepoValidationErrors(t *testing.T) {
|
||||||
store := openTestDB(t)
|
store := openTestDB(t)
|
||||||
packID := createTestPack(t, store)
|
packID := createTestPack(t, store)
|
||||||
|
|||||||
Reference in New Issue
Block a user