diff options
| author | 2026-04-02 07:51:14 +0000 | |
|---|---|---|
| committer | 2026-04-02 07:53:36 +0000 | |
| commit | 1013f61420d39fe491bd1e1af613c601dc5c3137 (patch) | |
| tree | 3171754f4404916b04d8f08b77bc629af51cefab | |
| parent | object/id: Add algorithm tests (diff) | |
| signature | No signature | |
ci: Stricter linting
| -rw-r--r-- | .golangci.yaml | 7 | ||||
| -rw-r--r-- | common/iowrap/write_flusher_nop.go | 2 | ||||
| -rw-r--r-- | object/id/algorithm_details.go | 28 | ||||
| -rw-r--r-- | object/id/algorithm_ops.go | 14 | ||||
| -rw-r--r-- | object/id/algorithm_test.go | 115 | ||||
| -rw-r--r-- | object/id/object_id_test.go | 122 |
6 files changed, 155 insertions, 133 deletions
diff --git a/.golangci.yaml b/.golangci.yaml index 254682fe..17338e52 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -8,8 +8,6 @@ linters: - internal/compress/internal disable: - dupword # extremely normal in tests and a pretty unnecessary linter - - dupl # i know when i'm duplicating things and it's almost always for a good reason - - ireturn # can't seem to ignore this linter properly and we don't need anyway, cf. tagged unions - goconst # unnecessary especially for our parsing code; many false positives - mnd # unnecessary especially for our parsing code; many false positives - lll # common sense is much better than these sort of rules @@ -21,13 +19,8 @@ linters: - cyclop # cyclomatic metrics aren't that good - godox # TODO/etc comments are allowed in our codebase - funlen # long functions are fine - - wrapcheck # rules around interface-return methods are a bit silly - dogsled # definitely not an issue, ignoring returns is normal - - exhaustruct # tmp: should fix... but too annoying at the moment - - err113 # tmp: will enable when we properly use defined errors - - gocognit # tmp: should consider sometime - settings: perfsprint: errorf: false diff --git a/common/iowrap/write_flusher_nop.go b/common/iowrap/write_flusher_nop.go index 91d30c3e..fba87e8d 100644 --- a/common/iowrap/write_flusher_nop.go +++ b/common/iowrap/write_flusher_nop.go @@ -7,6 +7,8 @@ type nopFlusher struct { } // NopFlush adapts writer into a [WriteFlusher] with a no-op Flush. +// +//nolint:ireturn func NopFlush(writer io.Writer) WriteFlusher { if writer == nil { return nil diff --git a/object/id/algorithm_details.go b/object/id/algorithm_details.go index d3fd665d..23737ab5 100644 --- a/object/id/algorithm_details.go +++ b/object/id/algorithm_details.go @@ -16,26 +16,46 @@ type algorithmDetails struct { emptyTree ObjectID } -func (algo Algorithm) info() algorithmDetails { +func (algo Algorithm) details() algorithmDetails { return algorithmTable[algo] } //nolint:gochecknoglobals var algorithmTable = [...]algorithmDetails{ - AlgorithmUnknown: {}, + AlgorithmUnknown: {}, //nolint:exhaustruct AlgorithmSHA1: { name: "sha1", size: sha1.Size, packHashID: 1, signatureHeaderName: "gpgsig", - new: sha1.New, + sum: func(data []byte) ObjectID { + sum := sha1.Sum(data) //#nosec G401 + + var id ObjectID + copy(id.data[:], sum[:]) + id.algo = AlgorithmSHA1 + + return id + }, + new: sha1.New, + emptyTree: ObjectID{}, //nolint:exhaustruct }, AlgorithmSHA256: { name: "sha256", size: sha256.Size, packHashID: 2, signatureHeaderName: "gpgsig-sha256", - new: sha256.New, + sum: func(data []byte) ObjectID { + sum := sha256.Sum256(data) + + var id ObjectID + copy(id.data[:], sum[:]) + id.algo = AlgorithmSHA256 + + return id + }, + new: sha256.New, + emptyTree: ObjectID{}, //nolint:exhaustruct }, } diff --git a/object/id/algorithm_ops.go b/object/id/algorithm_ops.go index b746dc5f..01d26c08 100644 --- a/object/id/algorithm_ops.go +++ b/object/id/algorithm_ops.go @@ -6,7 +6,7 @@ import "hash" // an empty tree ("tree 0\x00") // for this algorithm. func (algo Algorithm) EmptyTree() ObjectID { - return algo.info().emptyTree + return algo.details().emptyTree } // HexLen returns the encoded hexadecimal length. @@ -16,12 +16,12 @@ func (algo Algorithm) HexLen() int { // Size returns the hash size in bytes. func (algo Algorithm) Size() int { - return algo.info().size + return algo.details().size } // New returns a new hash.Hash for this algorithm. func (algo Algorithm) New() (hash.Hash, error) { - newFn := algo.info().new + newFn := algo.details().new if newFn == nil { return nil, ErrInvalidAlgorithm } @@ -33,22 +33,22 @@ func (algo Algorithm) New() (hash.Hash, error) { // // Unknown algorithms return 0. func (algo Algorithm) PackHashID() uint32 { - return algo.info().packHashID + return algo.details().packHashID } // SignatureHeaderName returns the signature header name for this algorithm. func (algo Algorithm) SignatureHeaderName() string { - return algo.info().signatureHeaderName + return algo.details().signatureHeaderName } // String returns the canonical algorithm name. func (algo Algorithm) String() string { - return algo.info().name + return algo.details().name } // Sum computes an object ID from raw data using the selected algorithm. func (algo Algorithm) Sum(data []byte) ObjectID { - return algo.info().sum(data) + return algo.details().sum(data) } // Zero returns the all-zero object ID for this algorithm. diff --git a/object/id/algorithm_test.go b/object/id/algorithm_test.go index 17d86805..15f61ecb 100644 --- a/object/id/algorithm_test.go +++ b/object/id/algorithm_test.go @@ -1,8 +1,6 @@ package id_test import ( - "bytes" - "strings" "testing" "codeberg.org/lindenii/furgit/object/id" @@ -59,119 +57,6 @@ func TestParseAlgorithm(t *testing.T) { } } -func TestFromHexRoundtrip(t *testing.T) { - t.Parallel() - - for _, algo := range id.SupportedAlgorithms() { - t.Run(algo.String(), func(t *testing.T) { - t.Parallel() - - hex := strings.Repeat("01", algo.Size()) - - i, err := id.FromHex(algo, hex) - if err != nil { - t.Fatalf("FromHex failed: %v", err) - } - - if got := i.String(); got != hex { - t.Fatalf("String() = %q, want %q", got, hex) - } - - if got := i.Algorithm().Size(); got != algo.Size() { - t.Fatalf("Size() = %d, want %d", got, algo.Size()) - } - - raw := i.Bytes() - if len(raw) != algo.Size() { - t.Fatalf("Bytes len = %d, want %d", len(raw), algo.Size()) - } - - id2, err := id.FromBytes(algo, raw) - if err != nil { - t.Fatalf("FromBytes failed: %v", err) - } - - if id2.String() != hex { - t.Fatalf("FromBytes roundtrip = %q, want %q", id2.String(), hex) - } - }) - } -} - -func TestFromHexErrors(t *testing.T) { - t.Parallel() - - t.Run("unknown algo", func(t *testing.T) { - t.Parallel() - - _, err := id.FromHex(id.AlgorithmUnknown, "00") - if err == nil { - t.Fatalf("expected FromHex error") - } - }) - // TODO: This may need to be revisited when hash-function-transition is implemented. - - for _, algo := range id.SupportedAlgorithms() { - t.Run(algo.String(), func(t *testing.T) { - t.Parallel() - - _, err := id.FromHex(algo, strings.Repeat("0", algo.HexLen()-1)) - if err == nil { - t.Fatalf("expected FromHex odd-len error") - } - - _, err = id.FromHex(algo, strings.Repeat("0", algo.HexLen()-2)) - if err == nil { - t.Fatalf("expected FromHex wrong-len error") - } - - _, err = id.FromHex(algo, "z"+strings.Repeat("0", algo.HexLen()-1)) - if err == nil { - t.Fatalf("expected FromHex invalid-hex error") - } - }) - } -} - -func TestFromBytesErrors(t *testing.T) { - t.Parallel() - - _, err := id.FromBytes(id.AlgorithmUnknown, []byte{1, 2}) - if err == nil { - t.Fatalf("expected FromBytes unknown algo error") - } - - for _, algo := range id.SupportedAlgorithms() { - _, err = id.FromBytes(algo, []byte{1, 2}) - if err == nil { - t.Fatalf("expected FromBytes wrong size error") - } - } -} - -func TestBytesReturnsCopy(t *testing.T) { - t.Parallel() - - for _, algo := range id.SupportedAlgorithms() { - id, err := id.FromHex(algo, strings.Repeat("01", algo.Size())) - if err != nil { - t.Fatalf("ParseHex failed: %v", err) - } - - b1 := id.Bytes() - - b2 := id.Bytes() - if !bytes.Equal(b1, b2) { - t.Fatalf("Bytes mismatch") - } - - b1[0] ^= 0xff - if bytes.Equal(b1, b2) { - t.Fatalf("Bytes should return independent copies") - } - } -} - func TestAlgorithmSum(t *testing.T) { t.Parallel() diff --git a/object/id/object_id_test.go b/object/id/object_id_test.go new file mode 100644 index 00000000..ba9413ba --- /dev/null +++ b/object/id/object_id_test.go @@ -0,0 +1,122 @@ +package id_test + +import ( + "bytes" + "strings" + "testing" + + "codeberg.org/lindenii/furgit/object/id" +) + +func TestFromBytesErrors(t *testing.T) { + t.Parallel() + + _, err := id.FromBytes(id.AlgorithmUnknown, []byte{1, 2}) + if err == nil { + t.Fatalf("expected FromBytes unknown algo error") + } + + for _, algo := range id.SupportedAlgorithms() { + _, err = id.FromBytes(algo, []byte{1, 2}) + if err == nil { + t.Fatalf("expected FromBytes wrong size error") + } + } +} + +func TestBytesReturnsCopy(t *testing.T) { + t.Parallel() + + for _, algo := range id.SupportedAlgorithms() { + id, err := id.FromHex(algo, strings.Repeat("01", algo.Size())) + if err != nil { + t.Fatalf("ParseHex failed: %v", err) + } + + b1 := id.Bytes() + + b2 := id.Bytes() + if !bytes.Equal(b1, b2) { + t.Fatalf("Bytes mismatch") + } + + b1[0] ^= 0xff + if bytes.Equal(b1, b2) { + t.Fatalf("Bytes should return independent copies") + } + } +} + +func TestFromHexErrors(t *testing.T) { + t.Parallel() + + t.Run("unknown algo", func(t *testing.T) { + t.Parallel() + + _, err := id.FromHex(id.AlgorithmUnknown, "00") + if err == nil { + t.Fatalf("expected FromHex error") + } + }) + // TODO: This may need to be revisited when hash-function-transition is implemented. + + for _, algo := range id.SupportedAlgorithms() { + t.Run(algo.String(), func(t *testing.T) { + t.Parallel() + + _, err := id.FromHex(algo, strings.Repeat("0", algo.HexLen()-1)) + if err == nil { + t.Fatalf("expected FromHex odd-len error") + } + + _, err = id.FromHex(algo, strings.Repeat("0", algo.HexLen()-2)) + if err == nil { + t.Fatalf("expected FromHex wrong-len error") + } + + _, err = id.FromHex(algo, "z"+strings.Repeat("0", algo.HexLen()-1)) + if err == nil { + t.Fatalf("expected FromHex invalid-hex error") + } + }) + } +} + +func TestFromHexRoundtrip(t *testing.T) { + t.Parallel() + + for _, algo := range id.SupportedAlgorithms() { + t.Run(algo.String(), func(t *testing.T) { + t.Parallel() + + hex := strings.Repeat("01", algo.Size()) + + i, err := id.FromHex(algo, hex) + if err != nil { + t.Fatalf("FromHex failed: %v", err) + } + + if got := i.String(); got != hex { + t.Fatalf("String() = %q, want %q", got, hex) + } + + if got := i.Algorithm().Size(); got != algo.Size() { + t.Fatalf("Size() = %d, want %d", got, algo.Size()) + } + + raw := i.Bytes() + if len(raw) != algo.Size() { + t.Fatalf("Bytes len = %d, want %d", len(raw), algo.Size()) + } + + id2, err := id.FromBytes(algo, raw) + if err != nil { + t.Fatalf("FromBytes failed: %v", err) + } + + if id2.String() != hex { + t.Fatalf("FromBytes roundtrip = %q, want %q", id2.String(), hex) + } + }) + } +} |
