diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index ca2f30e..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(TestDropWhile\"\ngo test -bench=\"BenchmarkTakeWhile)", - "Bash(gh pr create:*)" - ], - "deny": [], - "ask": [] - } -} diff --git a/.gitignore b/.gitignore index 804df32..b61dc6f 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ Temporary Items docs/public .trivycache/ .vscode/launch.json +.claude +AGENTS.md +bench*txt diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 38169f9..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,41 +0,0 @@ -# Repository Guidelines - -## Project Structure & Module Organization -- Source: package `underscore` in repo root (`*.go`). -- Tests: co-located `*_test.go` files in root; examples in `examples/`. -- Module: `github.com/rjNemo/underscore` (Go 1.24+). -- Docs: Hugo site under `docs/` (content, themes, public). -- CI/Security assets: Dockerfile(s), `.golangci.yml`, `.trivycache/`. - -## Build, Test, and Development Commands -- `go mod download`: fetch dependencies for local dev. -- `go test ./...`: run unit tests locally. -- `make build`: build Docker image `underscore:latest`. -- `make test`: run tests in Docker with coverage summary. -- `make docs`: serve docs locally (`cd docs && hugo server -D`). -- `make build-docs`: build static docs site. -- `make scan` / `make scan-config`: security scan with Trivy (image/config). - -## Coding Style & Naming Conventions -- Formatting: `gofmt`/`goimports` (enforced via `golangci-lint`). -- Lint: `golangci-lint run` (uses `.golangci.yml`). -- Indentation: tabs; idiomatic Go style. -- Naming: exported APIs use PascalCase (e.g., `Filter`, `Map`); files are lower_snake (`filter.go`). -- Imports: group stdlib/external/local; local prefix `github.com/rjNemo/underscore`. - -## Testing Guidelines -- Frameworks: Go `testing` with `testify` assertions. -- Conventions: `TestXxx` functions; prefer table-driven tests. -- Coverage: keep or increase overall coverage; `make test` prints summary. -- Run subset: `go test ./... -run TestFilter` for focused runs. - -## Commit & Pull Request Guidelines -- Commits: imperative, concise subject; include scope when helpful. -- Before PR: `golangci-lint run` and `make test` must pass. -- PRs: clear description, linked issues, tests for new/changed behavior, update README/docs when API changes. -- Reviews: follow CONTRIBUTING; require two approvals before merge. - -## Security & Configuration Tips -- Go 1.24+ recommended; Docker image pins Go 1.24. -- Do not commit secrets; prefer environment variables for local runs. -- Use `make scan` regularly; fix CRITICAL findings before release. diff --git a/bench_filter_after.txt b/bench_filter_after.txt deleted file mode 100644 index fdc0ed4..0000000 --- a/bench_filter_after.txt +++ /dev/null @@ -1,7 +0,0 @@ -goos: darwin -goarch: arm64 -pkg: github.com/rjNemo/underscore -cpu: Apple M1 Max -BenchmarkFilter-10 674666 1717 ns/op 8192 B/op 1 allocs/op -PASS -ok github.com/rjNemo/underscore 1.186s diff --git a/bench_filter_before.txt b/bench_filter_before.txt deleted file mode 100644 index 1e0490a..0000000 --- a/bench_filter_before.txt +++ /dev/null @@ -1,7 +0,0 @@ -goos: darwin -goarch: arm64 -pkg: github.com/rjNemo/underscore -cpu: Apple M1 Max -BenchmarkFilter-10 649467 1867 ns/op 8184 B/op 10 allocs/op -PASS -ok github.com/rjNemo/underscore 1.241s diff --git a/bench_flatmap_after.txt b/bench_flatmap_after.txt deleted file mode 100644 index 26c3627..0000000 --- a/bench_flatmap_after.txt +++ /dev/null @@ -1,7 +0,0 @@ -goos: darwin -goarch: arm64 -pkg: github.com/rjNemo/underscore -cpu: Apple M1 Max -BenchmarkFlatmap-10 2003317 616.7 ns/op 4992 B/op 2 allocs/op -PASS -ok github.com/rjNemo/underscore 1.850s diff --git a/bench_flatmap_before.txt b/bench_flatmap_before.txt deleted file mode 100644 index 76974a9..0000000 --- a/bench_flatmap_before.txt +++ /dev/null @@ -1,7 +0,0 @@ -goos: darwin -goarch: arm64 -pkg: github.com/rjNemo/underscore -cpu: Apple M1 Max -BenchmarkFlatmap-10 1380392 907.4 ns/op 6120 B/op 8 allocs/op -PASS -ok github.com/rjNemo/underscore 2.142s diff --git a/bench_orderby_after.txt b/bench_orderby_after.txt deleted file mode 100644 index 4aa2c81..0000000 --- a/bench_orderby_after.txt +++ /dev/null @@ -1,8 +0,0 @@ -goos: darwin -goarch: arm64 -pkg: github.com/rjNemo/underscore -cpu: Apple M1 Max -BenchmarkOrderBy-10 359067 3372 ns/op 8192 B/op 1 allocs/op -BenchmarkOrderBySmall-10 6726148 178.4 ns/op 80 B/op 1 allocs/op -PASS -ok github.com/rjNemo/underscore 2.635s diff --git a/bench_orderby_before.txt b/bench_orderby_before.txt deleted file mode 100644 index 10caa00..0000000 --- a/bench_orderby_before.txt +++ /dev/null @@ -1,8 +0,0 @@ -goos: darwin -goarch: arm64 -pkg: github.com/rjNemo/underscore -cpu: Apple M1 Max -BenchmarkOrderBy-10 564 2121531 ns/op 8201 B/op 1 allocs/op -BenchmarkOrderBySmall-10 6090744 199.0 ns/op 80 B/op 1 allocs/op -PASS -ok github.com/rjNemo/underscore 2.833s diff --git a/parallel_reduce_test.go b/parallel_reduce_test.go index e66079a..0d4ce43 100644 --- a/parallel_reduce_test.go +++ b/parallel_reduce_test.go @@ -2,7 +2,9 @@ package underscore_test import ( "context" + "errors" "testing" + "time" "github.com/stretchr/testify/assert" @@ -32,3 +34,138 @@ func TestParallelReduceEmpty(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 42, result) } + +func TestParallelReduceDefaultWorkers(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + ctx := context.Background() + + // Test with workers <= 0 to use GOMAXPROCS + result, err := u.ParallelReduce(ctx, nums, 0, func(ctx context.Context, n int, acc int) (int, error) { + return n + acc, nil + }, 0) + + assert.NoError(t, err) + assert.Greater(t, result, 0) +} + +func TestParallelReduceNegativeWorkers(t *testing.T) { + nums := []int{1, 2, 3} + ctx := context.Background() + + // Negative workers should default to GOMAXPROCS + result, err := u.ParallelReduce(ctx, nums, -1, func(ctx context.Context, n int, acc int) (int, error) { + return n + acc, nil + }, 0) + + assert.NoError(t, err) + assert.Greater(t, result, 0) +} + +func TestParallelReduceError(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + ctx := context.Background() + + expectedErr := errors.New("processing error") + _, err := u.ParallelReduce(ctx, nums, 2, func(ctx context.Context, n int, acc int) (int, error) { + if n == 3 { + return 0, expectedErr + } + return n + acc, nil + }, 0) + + assert.Error(t, err) + assert.Equal(t, expectedErr, err) +} + +func TestParallelReduceContextCancellation(t *testing.T) { + nums := make([]int, 100) + for i := range nums { + nums[i] = i + } + + ctx, cancel := context.WithCancel(context.Background()) + + // Cancel after a short delay + go func() { + time.Sleep(10 * time.Millisecond) + cancel() + }() + + _, err := u.ParallelReduce(ctx, nums, 4, func(ctx context.Context, n int, acc int) (int, error) { + // Slow processing to allow cancellation + time.Sleep(5 * time.Millisecond) + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + return n + acc, nil + } + }, 0) + + // Should either complete or get cancelled + if err != nil { + assert.ErrorIs(t, err, context.Canceled) + } +} + +func TestParallelReduceContextTimeout(t *testing.T) { + nums := make([]int, 20) + for i := range nums { + nums[i] = i + } + + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + + _, err := u.ParallelReduce(ctx, nums, 2, func(ctx context.Context, n int, acc int) (int, error) { + // Simulate slow work + time.Sleep(100 * time.Millisecond) + if ctx.Err() != nil { + return 0, ctx.Err() + } + return n + acc, nil + }, 0) + + // Should timeout + if err != nil { + assert.ErrorIs(t, err, context.DeadlineExceeded) + } +} + +func TestParallelReduceSingleElement(t *testing.T) { + ctx := context.Background() + result, err := u.ParallelReduce(ctx, []int{42}, 2, func(ctx context.Context, n int, acc int) (int, error) { + return n + acc, nil + }, 0) + + assert.NoError(t, err) + assert.Greater(t, result, 0) +} + +func TestParallelReduceManyWorkers(t *testing.T) { + nums := []int{1, 2, 3, 4, 5} + ctx := context.Background() + + // More workers than elements + result, err := u.ParallelReduce(ctx, nums, 10, func(ctx context.Context, n int, acc int) (int, error) { + return n + acc, nil + }, 0) + + assert.NoError(t, err) + assert.Greater(t, result, 0) +} + +func BenchmarkParallelReduce(b *testing.B) { + nums := make([]int, 100) + for i := range nums { + nums[i] = i + } + ctx := context.Background() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + u.ParallelReduce(ctx, nums, 4, func(ctx context.Context, n int, acc int) (int, error) { + return n + acc, nil + }, 0) + } +}