Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
100
backend/internal/credentialweight/weight.go
Normal file
100
backend/internal/credentialweight/weight.go
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// Package credentialweight defines shared credential weight validation and parsing.
|
||||
package credentialweight
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default is used when a credential does not define a weight.
|
||||
Default int64 = 1
|
||||
// Max bounds scheduler arithmetic while allowing practical proportional routing.
|
||||
Max int64 = 1_000_000
|
||||
)
|
||||
|
||||
// Normalize validates and normalizes an explicit weight. Non-positive values are
|
||||
// valid and normalize to zero, which excludes the credential from weighted routing.
|
||||
func Normalize(weight int64) (int64, error) {
|
||||
if weight <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if weight > Max {
|
||||
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
||||
}
|
||||
return weight, nil
|
||||
}
|
||||
|
||||
// ParseString parses a scheduler attribute. An empty value uses the default weight.
|
||||
func ParseString(raw string) (int64, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return Default, nil
|
||||
}
|
||||
weight, errParse := strconv.ParseInt(raw, 10, 64)
|
||||
if errParse != nil {
|
||||
return 0, fmt.Errorf("weight must be an integer: %w", errParse)
|
||||
}
|
||||
return Normalize(weight)
|
||||
}
|
||||
|
||||
// ParseValue parses a JSON-compatible auth-file metadata value.
|
||||
func ParseValue(value any) (int64, error) {
|
||||
switch typed := value.(type) {
|
||||
case int:
|
||||
return Normalize(int64(typed))
|
||||
case int8:
|
||||
return Normalize(int64(typed))
|
||||
case int16:
|
||||
return Normalize(int64(typed))
|
||||
case int32:
|
||||
return Normalize(int64(typed))
|
||||
case int64:
|
||||
return Normalize(typed)
|
||||
case uint:
|
||||
if uint64(typed) > uint64(Max) {
|
||||
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
||||
}
|
||||
return int64(typed), nil
|
||||
case uint8:
|
||||
return int64(typed), nil
|
||||
case uint16:
|
||||
return int64(typed), nil
|
||||
case uint32:
|
||||
if uint64(typed) > uint64(Max) {
|
||||
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
||||
}
|
||||
return int64(typed), nil
|
||||
case uint64:
|
||||
if typed > uint64(Max) {
|
||||
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
||||
}
|
||||
return int64(typed), nil
|
||||
case float64:
|
||||
if math.IsNaN(typed) || math.IsInf(typed, 0) || math.Trunc(typed) != typed {
|
||||
return 0, fmt.Errorf("weight must be an integer")
|
||||
}
|
||||
if typed <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if typed > float64(Max) {
|
||||
return 0, fmt.Errorf("weight must not exceed %d", Max)
|
||||
}
|
||||
return int64(typed), nil
|
||||
case float32:
|
||||
return ParseValue(float64(typed))
|
||||
case json.Number:
|
||||
weight, errParse := typed.Int64()
|
||||
if errParse != nil {
|
||||
return 0, fmt.Errorf("weight must be an integer: %w", errParse)
|
||||
}
|
||||
return Normalize(weight)
|
||||
case string:
|
||||
return ParseString(typed)
|
||||
default:
|
||||
return 0, fmt.Errorf("weight must be an integer")
|
||||
}
|
||||
}
|
||||
33
backend/internal/credentialweight/weight_test.go
Normal file
33
backend/internal/credentialweight/weight_test.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package credentialweight
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseValueValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value any
|
||||
want int64
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "default string", value: "", want: Default},
|
||||
{name: "negative excluded", value: json.Number("-5"), want: 0},
|
||||
{name: "fraction rejected", value: json.Number("1.5"), wantErr: true},
|
||||
{name: "maximum", value: json.Number("1000000"), want: Max},
|
||||
{name: "above maximum", value: json.Number("1000001"), wantErr: true},
|
||||
{name: "int64 overflow", value: json.Number("9223372036854775808"), wantErr: true},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, errParse := ParseValue(test.value)
|
||||
if (errParse != nil) != test.wantErr {
|
||||
t.Fatalf("ParseValue(%v) error = %v, wantErr=%v", test.value, errParse, test.wantErr)
|
||||
}
|
||||
if !test.wantErr && got != test.want {
|
||||
t.Fatalf("ParseValue(%v) = %d, want %d", test.value, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue