dynamo/deploy/operator/internal/checkpoint/hash_test.go

192 lines
5.6 KiB
Go

package checkpoint
import (
"testing"
nvidiacomv1alpha1 "github.com/ai-dynamo/dynamo/deploy/operator/api/v1alpha1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestComputeIdentityHash(t *testing.T) {
tests := []struct {
name string
identity nvidiacomv1alpha1.DynamoCheckpointIdentity
expectError bool
expectedHash string // Only set for deterministic checks
otherIdentity *nvidiacomv1alpha1.DynamoCheckpointIdentity
shouldMatch bool
}{
{
name: "basic identity produces deterministic hash",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
},
expectError: false,
expectedHash: "96429b2725761a09", // Known hash for this specific identity
},
{
name: "identity with all fields produces deterministic hash",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-13b-hf",
BackendFramework: "sglang",
DynamoVersion: "0.4.2",
TensorParallelSize: 2,
PipelineParallelSize: 1,
Dtype: "float16",
MaxModelLen: 4096,
ExtraParameters: map[string]string{
"gpu_memory_utilization": "0.9",
},
},
expectError: false,
expectedHash: "f4ba65bccbb8e4fb", // Known hash for this specific identity
},
{
name: "same identity produces same hash",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
},
otherIdentity: &nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
},
expectError: false,
shouldMatch: true,
},
{
name: "different models produce different hashes",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
},
otherIdentity: &nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-13b-hf",
BackendFramework: "vllm",
},
expectError: false,
shouldMatch: false,
},
{
name: "different frameworks produce different hashes",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
},
otherIdentity: &nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "sglang",
},
expectError: false,
shouldMatch: false,
},
{
name: "normalization: zero vs unset numeric fields",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
TensorParallelSize: 0,
MaxModelLen: 0,
},
otherIdentity: &nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
// TensorParallelSize and MaxModelLen omitted (defaults to 0)
},
expectError: false,
shouldMatch: true,
},
{
name: "normalization: empty vs nil map",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
ExtraParameters: map[string]string{},
},
otherIdentity: &nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
ExtraParameters: nil,
},
expectError: false,
shouldMatch: true,
},
{
name: "extra parameters order should not matter",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
ExtraParameters: map[string]string{
"param_a": "value1",
"param_b": "value2",
},
},
otherIdentity: &nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
ExtraParameters: map[string]string{
"param_b": "value2",
"param_a": "value1",
},
},
expectError: false,
shouldMatch: true,
},
{
name: "different extra parameters produce different hashes",
identity: nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
ExtraParameters: map[string]string{
"gpu_memory_utilization": "0.9",
},
},
otherIdentity: &nvidiacomv1alpha1.DynamoCheckpointIdentity{
Model: "meta-llama/Llama-2-7b-hf",
BackendFramework: "vllm",
ExtraParameters: map[string]string{
"gpu_memory_utilization": "0.8",
},
},
expectError: false,
shouldMatch: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hash1, err1 := ComputeIdentityHash(tt.identity)
if tt.expectError {
require.Error(t, err1)
return
}
require.NoError(t, err1)
assert.NotEmpty(t, hash1, "hash should not be empty")
assert.Len(t, hash1, 16, "hash should be 16 characters (64 bits)")
// Verify it's hex
assert.Regexp(t, "^[0-9a-f]{16}$", hash1, "hash should be 16 hex characters")
// If we have an expected hash, check it
if tt.expectedHash != "" {
assert.Equal(t, tt.expectedHash, hash1)
}
// If we have another identity to compare, compute its hash
if tt.otherIdentity != nil {
hash2, err2 := ComputeIdentityHash(*tt.otherIdentity)
require.NoError(t, err2)
if tt.shouldMatch {
assert.Equal(t, hash1, hash2, "hashes should match")
} else {
assert.NotEqual(t, hash1, hash2, "hashes should differ")
}
}
})
}
}