loader logic revised
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
package connector
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"galaxy/model/client"
|
||||
"galaxy/model/report"
|
||||
)
|
||||
@@ -29,7 +33,72 @@ type UIConnector interface {
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
OS string `json:"os"` // Operating System name (unix, darwin, windows, etc.)
|
||||
Version string `json:"version"` // Semver format: X.Y.Z
|
||||
URL string `json:"url"` // Artifact download URL for this version
|
||||
OS string `json:"os"` // Operating System name (unix, darwin, windows, etc.)
|
||||
Version string `json:"version"` // Semver format: X.Y.Z
|
||||
URL string `json:"url"` // Artifact download URL for this version
|
||||
Checksum SHA256Digest `json:"sha256"` // Base64 SHA-256 checksum for artifact binary data
|
||||
}
|
||||
|
||||
// SHA256Digest represents a SHA-256 digest in raw binary form.
|
||||
//
|
||||
// Internally it stores the exact 32-byte digest.
|
||||
// In JSON it is encoded as a lowercase hexadecimal string of 64 characters.
|
||||
type SHA256Digest [32]byte
|
||||
|
||||
// NewSHA256Digest calculates SHA-256 for the provided byte slice
|
||||
// and returns the digest as SHA256Digest.
|
||||
//
|
||||
// The function does not modify the input data.
|
||||
func NewSHA256Digest(data []byte) SHA256Digest {
|
||||
sum := sha256.Sum256(data)
|
||||
return SHA256Digest(sum)
|
||||
}
|
||||
|
||||
// String returns the lowercase hexadecimal representation
|
||||
// of the digest.
|
||||
//
|
||||
// The returned string always contains exactly 64 characters.
|
||||
func (d SHA256Digest) String() string {
|
||||
return hex.EncodeToString(d[:])
|
||||
}
|
||||
|
||||
// MarshalJSON encodes the digest as a JSON string containing
|
||||
// the lowercase hexadecimal SHA-256 value.
|
||||
//
|
||||
// Example JSON value:
|
||||
//
|
||||
// "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
|
||||
func (d SHA256Digest) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(d.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a JSON string containing a lowercase or uppercase
|
||||
// hexadecimal SHA-256 value into the digest.
|
||||
//
|
||||
// The input must be a JSON string with exactly 64 hexadecimal characters.
|
||||
func (d *SHA256Digest) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return fmt.Errorf("sha256 digest must be a JSON string: %w", err)
|
||||
}
|
||||
|
||||
if len(s) != hex.EncodedLen(len(d)) {
|
||||
return fmt.Errorf("invalid SHA-256 hex length: got %d, want %d", len(s), hex.EncodedLen(len(d)))
|
||||
}
|
||||
|
||||
decoded, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid SHA-256 hex value: %w", err)
|
||||
}
|
||||
|
||||
copy(d[:], decoded)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Equal returns true when both digests are identical.
|
||||
//
|
||||
// Since SHA256Digest is based on a fixed-size array, direct value comparison
|
||||
// is efficient and idiomatic for non-constant-time equality checks.
|
||||
func (d SHA256Digest) Equal(other SHA256Digest) bool {
|
||||
return d == other
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package connector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestSHA256DigestMarshalJSON verifies that the digest is encoded
|
||||
// as a lowercase hexadecimal JSON string.
|
||||
func TestSHA256DigestMarshalJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
digest := NewSHA256Digest([]byte("hello world"))
|
||||
|
||||
data, err := json.Marshal(digest)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, `"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"`, string(data))
|
||||
}
|
||||
|
||||
// TestSHA256DigestUnmarshalJSON verifies that a valid hexadecimal JSON string
|
||||
// is decoded back into the original digest.
|
||||
func TestSHA256DigestUnmarshalJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var digest SHA256Digest
|
||||
err := json.Unmarshal(
|
||||
[]byte(`"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"`),
|
||||
&digest,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := NewSHA256Digest([]byte("hello world"))
|
||||
require.True(t, digest.Equal(expected))
|
||||
}
|
||||
|
||||
// TestSHA256DigestUnmarshalJSONInvalidLength verifies that invalid digest length
|
||||
// is rejected.
|
||||
func TestSHA256DigestUnmarshalJSONInvalidLength(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var digest SHA256Digest
|
||||
err := json.Unmarshal([]byte(`"abcd"`), &digest)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "invalid SHA-256 hex length")
|
||||
}
|
||||
|
||||
// TestSHA256DigestUnmarshalJSONInvalidHex verifies that non-hexadecimal input
|
||||
// is rejected.
|
||||
func TestSHA256DigestUnmarshalJSONInvalidHex(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var digest SHA256Digest
|
||||
err := json.Unmarshal(
|
||||
[]byte(`"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"`),
|
||||
&digest,
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "invalid SHA-256 hex value")
|
||||
}
|
||||
|
||||
// TestFileMetadataJSONRoundTrip verifies that a struct containing the digest
|
||||
// round-trips correctly through JSON.
|
||||
func TestFileMetadataJSONRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
original := VersionInfo{
|
||||
OS: "linux",
|
||||
Version: "1.2.3",
|
||||
URL: "http://server:8080",
|
||||
Checksum: NewSHA256Digest([]byte("payload")),
|
||||
}
|
||||
|
||||
data, err := json.Marshal(original)
|
||||
require.NoError(t, err)
|
||||
|
||||
var decoded VersionInfo
|
||||
err = json.Unmarshal(data, &decoded)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, original.OS, decoded.OS)
|
||||
require.Equal(t, original.Version, decoded.Version)
|
||||
require.Equal(t, original.URL, decoded.URL)
|
||||
require.True(t, original.Checksum.Equal(decoded.Checksum))
|
||||
}
|
||||
Reference in New Issue
Block a user