fix(supply-api): restore uncached build health

This commit is contained in:
Your Name
2026-04-17 16:20:34 +08:00
parent ad776e4079
commit 2e0f6e29aa
5 changed files with 43 additions and 14 deletions

View File

@@ -2,6 +2,11 @@ package main
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"os/exec"
"path/filepath"
@@ -12,7 +17,7 @@ import (
func TestMain_ProdStartupFailsWhenDatabaseUnavailable(t *testing.T) {
configPath := filepath.Join(t.TempDir(), "config.prod.yaml")
content := []byte(`
content := []byte(fmt.Sprintf(`
server:
addr: "127.0.0.1:0"
shutdown_timeout: 1s
@@ -28,9 +33,10 @@ redis:
port: 1
token:
issuer: "prod-issuer"
secret_key: "prod-secret"
algorithm: "HS256"
`)
algorithm: "RS256"
public_key: |
%s
`, indentYAMLBlock(mustGenerateRSAPublicKeyPEM(t), " ")))
if err := os.WriteFile(configPath, content, 0o600); err != nil {
t.Fatalf("failed to write config file: %v", err)
}
@@ -88,3 +94,25 @@ func TestMainHelperProcess(t *testing.T) {
main()
os.Exit(0)
}
func mustGenerateRSAPublicKeyPEM(t *testing.T) string {
t.Helper()
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatalf("failed to generate RSA key: %v", err)
}
der, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
if err != nil {
t.Fatalf("failed to marshal RSA public key: %v", err)
}
return string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}))
}
func indentYAMLBlock(value, indent string) string {
lines := strings.Split(strings.TrimRight(value, "\n"), "\n")
for i, line := range lines {
lines[i] = indent + line
}
return strings.Join(lines, "\n")
}

View File

@@ -135,7 +135,7 @@ token:
}
}
func TestLoadFromPath_ProdRejectsMissingHS256SecretKey(t *testing.T) {
func TestLoadFromPath_ProdRejectsHS256Algorithm(t *testing.T) {
// 清除环境变量以确保测试隔离
origVal := os.Getenv("SUPPLY_TOKEN_SECRET_KEY")
os.Unsetenv("SUPPLY_TOKEN_SECRET_KEY")
@@ -166,10 +166,10 @@ token:
_, err := LoadFromPath("prod", configPath)
if err == nil {
t.Fatal("expected prod config without HS256 secret key to return error")
t.Fatal("expected prod config with HS256 algorithm to return error")
}
if !strings.Contains(err.Error(), "token.secret_key") {
t.Fatalf("expected error to mention token.secret_key, got %v", err)
if !strings.Contains(err.Error(), "token.algorithm") {
t.Fatalf("expected error to mention token.algorithm, got %v", err)
}
}

View File

@@ -8,6 +8,7 @@ import (
"strings"
"testing"
"lijiaoqiao/supply-api/internal/pkg/pathutil"
"lijiaoqiao/supply-api/internal/iam/service"
"lijiaoqiao/supply-api/internal/middleware"
@@ -832,7 +833,7 @@ func TestExtractRoleCode(t *testing.T) {
// func TestExtractRoleCodeFromUserPath(t *testing.T) { ... }
func TestSplitPath(t *testing.T) {
result := splitPath("/api/v1/iam/roles/developer")
result := pathutil.SplitPath("/api/v1/iam/roles/developer")
assert.Equal(t, []string{"api", "v1", "iam", "roles", "developer"}, result)
}
@@ -1267,4 +1268,3 @@ func TestToRoleResponse(t *testing.T) {
assert.Equal(t, 20, response.Level)
assert.True(t, response.IsActive)
}

View File

@@ -1,8 +1,6 @@
// Package pathutil provides path manipulation utilities.
package pathutil
import "strings"
// SplitPath splits a URL or file path by '/' and returns non-empty segments.
// Unlike strings.Split, this skips empty segments from leading/trailing/consecutive slashes.
func SplitPath(path string) []string {

View File

@@ -4,13 +4,14 @@ import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/hkdf"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"golang.org/x/crypto/hkdf"
)
// ==================== P0-02 KMS加密方案 ====================
@@ -207,7 +208,9 @@ func deriveDEK(keyID string, version int) []byte {
ikm := append([]byte(keyID), byte(version&0xff))
hkdfReader := hkdf.New(sha256.New, ikm, nil, []byte("supply-api-dek-v1"))
hkdfReader.Read(masterKey)
if _, err := io.ReadFull(hkdfReader, masterKey); err != nil {
panic(fmt.Sprintf("failed to derive DEK: %v", err))
}
return masterKey
}