diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3d4210a..651bb76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,21 +14,21 @@ jobs: contents: read steps: - - name: Check out code - uses: actions/checkout@v4 - with: - persist-credentials: false + - name: Check out code + uses: actions/checkout@v4 + with: + persist-credentials: false - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: 'go.mod' + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' - - name: Run tests - run: make test + - name: Run tests + run: make test - - name: Upload coverage - uses: codecov/codecov-action@v4 - if: success() - with: - fail_ci_if_error: false + - name: Upload coverage + uses: codecov/codecov-action@v4 + if: success() + with: + fail_ci_if_error: false diff --git a/go.mod b/go.mod index cffe51e..84e8a72 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/codeGROOVE-dev/prcost go 1.25.3 require ( - github.com/codeGROOVE-dev/ds9 v0.0.0-20251028153329-0fbc86f835ed + github.com/codeGROOVE-dev/ds9 v0.5.0 github.com/codeGROOVE-dev/gsm v0.0.0-20251019065141-833fe2363d22 - github.com/codeGROOVE-dev/prx v0.0.0-20251027204543-4e6165f046e5 + github.com/codeGROOVE-dev/prx v0.0.0-20251028202628-9f237ee71356 github.com/codeGROOVE-dev/turnclient v0.0.0-20251028130307-1f85c9aa43c4 golang.org/x/time v0.14.0 ) diff --git a/go.sum b/go.sum index a3f8548..d56c1dd 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,11 @@ -github.com/codeGROOVE-dev/ds9 v0.0.0-20251028153329-0fbc86f835ed h1:PNvhCROSwAybWrJwYkTBUAaydQKJ8dWD8ml7gde5nkA= -github.com/codeGROOVE-dev/ds9 v0.0.0-20251028153329-0fbc86f835ed/go.mod h1:/hZt40fp5FfuzVwiw9fgoOMBAa260rKPiWNJt8RU10Y= +github.com/codeGROOVE-dev/ds9 v0.5.0 h1:J5IX1S1HtuJjGvj/gaMCXMY0HQb5Gzqz5c1a2uz14VM= +github.com/codeGROOVE-dev/ds9 v0.5.0/go.mod h1:/hZt40fp5FfuzVwiw9fgoOMBAa260rKPiWNJt8RU10Y= github.com/codeGROOVE-dev/gsm v0.0.0-20251019065141-833fe2363d22 h1:gtN3rOc6YspO646BkcOxBhPjEqKUz+jl175jIqglfDg= github.com/codeGROOVE-dev/gsm v0.0.0-20251019065141-833fe2363d22/go.mod h1:KV+w19ubP32PxZPE1hOtlCpTaNpF0Bpb32w5djO8UTg= -github.com/codeGROOVE-dev/prx v0.0.0-20251027012315-7b273aabfc7d h1:kUaCKFRxWFrWEyl4fVHi+eY/D5tKhBU29a8YbQyihEk= -github.com/codeGROOVE-dev/prx v0.0.0-20251027012315-7b273aabfc7d/go.mod h1:7qLbi18baOyS8yO/6/64SBIqtyzSzLFdsDST15NPH3w= -github.com/codeGROOVE-dev/prx v0.0.0-20251027204543-4e6165f046e5 h1:tjxTLJ5NXx1xhReL4M+J4LTl/JGNSZjPrznAoci06OA= -github.com/codeGROOVE-dev/prx v0.0.0-20251027204543-4e6165f046e5/go.mod h1:FEy3gz9IYDXWnKWkoDSL+pWu6rujxbBSrF4w5A8QSK0= -github.com/codeGROOVE-dev/retry v1.2.0 h1:xYpYPX2PQZmdHwuiQAGGzsBm392xIMl4nfMEFApQnu8= -github.com/codeGROOVE-dev/retry v1.2.0/go.mod h1:8OgefgV1XP7lzX2PdKlCXILsYKuz6b4ZpHa/20iLi8E= +github.com/codeGROOVE-dev/prx v0.0.0-20251028202628-9f237ee71356 h1:lHoHnylLAp7/7BMhdiTh9Z2+p4ATcQ7aFcgqxOFGzE4= +github.com/codeGROOVE-dev/prx v0.0.0-20251028202628-9f237ee71356/go.mod h1:FEy3gz9IYDXWnKWkoDSL+pWu6rujxbBSrF4w5A8QSK0= github.com/codeGROOVE-dev/retry v1.3.0 h1:/+ipAWRJLL6y1R1vprYo0FSjSBvH6fE5j9LKXjpD54g= github.com/codeGROOVE-dev/retry v1.3.0/go.mod h1:8OgefgV1XP7lzX2PdKlCXILsYKuz6b4ZpHa/20iLi8E= -github.com/codeGROOVE-dev/turnclient v0.0.0-20251022064427-5a712e1e10e6 h1:7FCmaftkl362oTZHVJyUg+xhxqfQFx+JisBf7RgklL8= -github.com/codeGROOVE-dev/turnclient v0.0.0-20251022064427-5a712e1e10e6/go.mod h1:fYwtN9Ql6lY8t2WvCfENx+mP5FUwjlqwXCLx9CVLY20= github.com/codeGROOVE-dev/turnclient v0.0.0-20251028130307-1f85c9aa43c4 h1:si9tMEo5SXpDuDXGkJ1zNnnpP8TbmakrkNujAbpKlqA= github.com/codeGROOVE-dev/turnclient v0.0.0-20251028130307-1f85c9aa43c4/go.mod h1:bFWMd0JeaJY0kSIO5AcRQdJLXF3Fo3eKclE49vmIZes= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= diff --git a/internal/server/server.go b/internal/server/server.go index 7614745..bf8d99d 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -20,7 +20,7 @@ import ( "sync" "time" - "github.com/codeGROOVE-dev/ds9" + "github.com/codeGROOVE-dev/ds9/pkg/datastore" "github.com/codeGROOVE-dev/gsm" "github.com/codeGROOVE-dev/prcost/pkg/cost" "github.com/codeGROOVE-dev/prcost/pkg/github" @@ -117,7 +117,7 @@ type Server struct { prDataCacheMu sync.RWMutex calcResultCacheMu sync.RWMutex // DataStore client for persistent caching (nil if not enabled). - dsClient *ds9.Client + dsClient *datastore.Client } // CalculateRequest represents a request to calculate PR costs. @@ -236,7 +236,7 @@ func New() *Server { // Initialize DataStore client if DATASTORE_DB is set (persistent caching across restarts). if dbID := os.Getenv("DATASTORE_DB"); dbID != "" { - dsClient, err := ds9.NewClientWithDatabase(ctx, "", dbID) + dsClient, err := datastore.NewClientWithDatabase(ctx, "", dbID) if err != nil { logger.WarnContext(ctx, "Failed to initialize DataStore client - persistent caching disabled", "database_id", dbID, "error", err) @@ -374,11 +374,11 @@ func (s *Server) cachedPRQuery(ctx context.Context, key string) ([]github.PRSumm return nil, false } - dsKey := ds9.NameKey("PRQueryCache", key, nil) + dsKey := datastore.NameKey("PRQueryCache", key, nil) var entity prQueryCacheEntity err := s.dsClient.Get(ctx, dsKey, &entity) if err != nil { - if !errors.Is(err, ds9.ErrNoSuchEntity) { + if !errors.Is(err, datastore.ErrNoSuchEntity) { s.logger.WarnContext(ctx, "DataStore cache read failed", "key", key, "error", err) } return nil, false @@ -452,7 +452,7 @@ func (s *Server) cachePRQuery(ctx context.Context, key string, prs []github.PRSu QueryKey: key, } - dsKey := ds9.NameKey("PRQueryCache", key, nil) + dsKey := datastore.NameKey("PRQueryCache", key, nil) if _, err := s.dsClient.Put(ctx, dsKey, &entity); err != nil { s.logger.WarnContext(ctx, "Failed to write PR query to DataStore", "key", key, "error", err) return @@ -482,11 +482,11 @@ func (s *Server) cachedPRData(ctx context.Context, key string) (cost.PRData, boo return cost.PRData{}, false } - dsKey := ds9.NameKey("PRDataCache", key, nil) + dsKey := datastore.NameKey("PRDataCache", key, nil) var entity prDataCacheEntity err := s.dsClient.Get(ctx, dsKey, &entity) if err != nil { - if !errors.Is(err, ds9.ErrNoSuchEntity) { + if !errors.Is(err, datastore.ErrNoSuchEntity) { s.logger.WarnContext(ctx, "DataStore cache read failed", "key", key, "error", err) } return cost.PRData{}, false @@ -542,7 +542,7 @@ func (s *Server) cachePRData(ctx context.Context, key string, prData cost.PRData URL: key, } - dsKey := ds9.NameKey("PRDataCache", key, nil) + dsKey := datastore.NameKey("PRDataCache", key, nil) if _, err := s.dsClient.Put(ctx, dsKey, &entity); err != nil { s.logger.WarnContext(ctx, "Failed to write PR data to DataStore", "key", key, "error", err) return @@ -586,11 +586,11 @@ func (s *Server) cachedCalcResult(ctx context.Context, prURL string, cfg cost.Co return cost.Breakdown{}, false } - dsKey := ds9.NameKey("CalcResultCache", key, nil) + dsKey := datastore.NameKey("CalcResultCache", key, nil) var entity calcResultCacheEntity err := s.dsClient.Get(ctx, dsKey, &entity) if err != nil { - if !errors.Is(err, ds9.ErrNoSuchEntity) { + if !errors.Is(err, datastore.ErrNoSuchEntity) { s.logger.WarnContext(ctx, "DataStore calc cache read failed", "key", key, "error", err) } return cost.Breakdown{}, false @@ -646,7 +646,7 @@ func (s *Server) cacheCalcResult(ctx context.Context, prURL string, cfg cost.Con ConfigKey: configHash(cfg), } - dsKey := ds9.NameKey("CalcResultCache", key, nil) + dsKey := datastore.NameKey("CalcResultCache", key, nil) if _, err := s.dsClient.Put(ctx, dsKey, &entity); err != nil { s.logger.WarnContext(ctx, "Failed to write calc result to DataStore", "key", key, "error", err) return diff --git a/pkg/cost/cost_test.go b/pkg/cost/cost_test.go index bcf2fe5..2ca4c55 100644 --- a/pkg/cost/cost_test.go +++ b/pkg/cost/cost_test.go @@ -7,6 +7,7 @@ import ( "log/slog" "os" "strings" + "sync" "testing" "time" ) @@ -702,6 +703,7 @@ func TestCalculateFastTurnaroundNoDelay(t *testing.T) { // Mock PRFetcher for testing AnalyzePRs type mockPRFetcher struct { + mu sync.Mutex data map[string]PRData failURLs map[string]error callCount int @@ -710,10 +712,14 @@ type mockPRFetcher struct { } func (m *mockPRFetcher) FetchPRData(ctx context.Context, prURL string, updatedAt time.Time) (PRData, error) { + m.mu.Lock() m.callCount++ + callCount := m.callCount + maxCalls := m.maxCalls + m.mu.Unlock() // Fail after max calls if set - if m.maxCalls > 0 && m.callCount > m.maxCalls { + if maxCalls > 0 && callCount > maxCalls { return PRData{}, errors.New("max calls exceeded") }