mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-04-02 03:01:13 -04:00
## CHANGES - Add NaN and Infinity validation for float64 values - Add NaN and Infinity validation for float32 values - Add negative value check for int64 type - Add negative value check for json.Number type - Add comprehensive test cases for special float values - Add test cases for negative int64 and json.Number inputs - Update line reference comments for validation checks
364 lines
8.3 KiB
Go
364 lines
8.3 KiB
Go
package restapi
|
|
|
|
import (
|
|
"encoding/json"
|
|
"math"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestBuildFabricChatURL(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
addr string
|
|
want string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "empty address",
|
|
addr: "",
|
|
want: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "valid http URL",
|
|
addr: "http://localhost:8080",
|
|
want: "http://localhost:8080",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid https URL",
|
|
addr: "https://api.example.com",
|
|
want: "https://api.example.com",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "http URL with trailing slash",
|
|
addr: "http://localhost:8080/",
|
|
want: "http://localhost:8080",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "malformed URL - missing host",
|
|
addr: "http://",
|
|
want: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "malformed URL - port only with http",
|
|
addr: "https://:8080",
|
|
want: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "colon-prefixed port",
|
|
addr: ":8080",
|
|
want: "http://127.0.0.1:8080",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "bare host:port",
|
|
addr: "localhost:8080",
|
|
want: "http://localhost:8080",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "bare hostname",
|
|
addr: "localhost",
|
|
want: "http://localhost",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "IP address with port",
|
|
addr: "192.168.1.1:3000",
|
|
want: "http://192.168.1.1:3000",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "bare address with path - invalid",
|
|
addr: "localhost:8080/some/path",
|
|
want: "",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "bare hostname with path - invalid",
|
|
addr: "localhost/api",
|
|
want: "",
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := buildFabricChatURL(tt.addr)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("buildFabricChatURL() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("buildFabricChatURL() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseOllamaNumCtx(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
options map[string]any
|
|
want int
|
|
wantErr bool
|
|
errMsg string
|
|
}{
|
|
// --- Valid inputs ---
|
|
{
|
|
name: "nil options",
|
|
options: nil,
|
|
want: 0,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "empty options",
|
|
options: map[string]any{},
|
|
want: 0,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "num_ctx not present",
|
|
options: map[string]any{"other_key": 123},
|
|
want: 0,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "num_ctx is null",
|
|
options: map[string]any{"num_ctx": nil},
|
|
want: 0,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid int",
|
|
options: map[string]any{"num_ctx": 4096},
|
|
want: 4096,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid float64 (whole number)",
|
|
options: map[string]any{"num_ctx": float64(8192)},
|
|
want: 8192,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid float32 (whole number)",
|
|
options: map[string]any{"num_ctx": float32(2048)},
|
|
want: 2048,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid json.Number",
|
|
options: map[string]any{"num_ctx": json.Number("16384")},
|
|
want: 16384,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid string",
|
|
options: map[string]any{"num_ctx": "32768"},
|
|
want: 32768,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid int64",
|
|
options: map[string]any{"num_ctx": int64(65536)},
|
|
want: 65536,
|
|
wantErr: false,
|
|
},
|
|
// --- Invalid inputs ---
|
|
{
|
|
name: "float64 with fractional part",
|
|
options: map[string]any{"num_ctx": 4096.5},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be an integer, got float with fractional part",
|
|
},
|
|
{
|
|
name: "float32 with fractional part",
|
|
options: map[string]any{"num_ctx": float32(2048.75)},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be an integer, got float with fractional part",
|
|
},
|
|
{
|
|
name: "negative int",
|
|
options: map[string]any{"num_ctx": -100},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be positive",
|
|
},
|
|
{
|
|
name: "zero int",
|
|
options: map[string]any{"num_ctx": 0},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be positive",
|
|
},
|
|
{
|
|
name: "negative float64",
|
|
options: map[string]any{"num_ctx": float64(-500)},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be positive",
|
|
},
|
|
{
|
|
name: "negative float32",
|
|
options: map[string]any{"num_ctx": float32(-250)},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be positive",
|
|
},
|
|
{
|
|
name: "non-numeric string",
|
|
options: map[string]any{"num_ctx": "not-a-number"},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a valid number",
|
|
},
|
|
{
|
|
name: "invalid json.Number",
|
|
options: map[string]any{"num_ctx": json.Number("invalid")},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a valid number",
|
|
},
|
|
{
|
|
name: "exceeds maximum allowed value",
|
|
options: map[string]any{"num_ctx": 2000000},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx exceeds maximum allowed value",
|
|
},
|
|
{
|
|
name: "unsupported type (bool)",
|
|
options: map[string]any{"num_ctx": true},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a number, got invalid type",
|
|
},
|
|
{
|
|
name: "unsupported type (slice)",
|
|
options: map[string]any{"num_ctx": []int{1, 2, 3}},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a number, got invalid type",
|
|
},
|
|
// --- Edge cases ---
|
|
{
|
|
name: "minimum valid value",
|
|
options: map[string]any{"num_ctx": 1},
|
|
want: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "maximum allowed value",
|
|
options: map[string]any{"num_ctx": 1000000},
|
|
want: 1000000,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "very large float64 (overflow)",
|
|
options: map[string]any{"num_ctx": float64(math.MaxFloat64)},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx value out of range",
|
|
},
|
|
{
|
|
name: "large int64 exceeding maxInt on 32-bit",
|
|
options: map[string]any{"num_ctx": int64(1 << 40)},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx", // either "too large" or "exceeds maximum"
|
|
},
|
|
{
|
|
name: "long string gets truncated in error",
|
|
options: map[string]any{"num_ctx": "this-is-a-very-long-string-that-should-be-truncated-in-the-error-message"},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a valid number",
|
|
},
|
|
// --- Special float values ---
|
|
{
|
|
name: "float64 NaN",
|
|
options: map[string]any{"num_ctx": math.NaN()},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a finite number",
|
|
},
|
|
{
|
|
name: "float64 positive infinity",
|
|
options: map[string]any{"num_ctx": math.Inf(1)},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a finite number",
|
|
},
|
|
{
|
|
name: "float64 negative infinity",
|
|
options: map[string]any{"num_ctx": math.Inf(-1)},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a finite number",
|
|
},
|
|
{
|
|
name: "float32 NaN",
|
|
options: map[string]any{"num_ctx": float32(math.NaN())},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a finite number",
|
|
},
|
|
{
|
|
name: "float32 positive infinity",
|
|
options: map[string]any{"num_ctx": float32(math.Inf(1))},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a finite number",
|
|
},
|
|
{
|
|
name: "float32 negative infinity",
|
|
options: map[string]any{"num_ctx": float32(math.Inf(-1))},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be a finite number",
|
|
},
|
|
// --- Negative int64 (32-bit wraparound prevention) ---
|
|
{
|
|
name: "negative int64",
|
|
options: map[string]any{"num_ctx": int64(-1000)},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be positive",
|
|
},
|
|
{
|
|
name: "negative json.Number",
|
|
options: map[string]any{"num_ctx": json.Number("-500")},
|
|
want: 0,
|
|
wantErr: true,
|
|
errMsg: "num_ctx must be positive",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := parseOllamaNumCtx(tt.options)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("parseOllamaNumCtx() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if err != nil && tt.errMsg != "" {
|
|
if !strings.Contains(err.Error(), tt.errMsg) {
|
|
t.Errorf("parseOllamaNumCtx() error message = %q, want to contain %q", err.Error(), tt.errMsg)
|
|
}
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("parseOllamaNumCtx() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|