| 1 | package config |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | |
| 7 | "github.com/ipfs/boxo/ipld/unixfs/io" |
| 8 | mh "github.com/multiformats/go-multihash" |
| 9 | ) |
| 10 | |
| 11 | func TestValidateImportConfig_HAMTFanout(t *testing.T) { |
| 12 | tests := []struct { |
| 13 | name string |
| 14 | fanout int64 |
| 15 | wantErr bool |
| 16 | errMsg string |
| 17 | }{ |
| 18 | // Valid values - powers of 2, multiples of 8, and <= 1024 |
| 19 | {name: "valid 8", fanout: 8, wantErr: false}, |
| 20 | {name: "valid 16", fanout: 16, wantErr: false}, |
| 21 | {name: "valid 32", fanout: 32, wantErr: false}, |
| 22 | {name: "valid 64", fanout: 64, wantErr: false}, |
| 23 | {name: "valid 128", fanout: 128, wantErr: false}, |
| 24 | {name: "valid 256", fanout: 256, wantErr: false}, |
| 25 | {name: "valid 512", fanout: 512, wantErr: false}, |
| 26 | {name: "valid 1024", fanout: 1024, wantErr: false}, |
| 27 | |
| 28 | // Invalid values - not powers of 2 |
| 29 | {name: "invalid 7", fanout: 7, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 30 | {name: "invalid 15", fanout: 15, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 31 | {name: "invalid 100", fanout: 100, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 32 | {name: "invalid 257", fanout: 257, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 33 | {name: "invalid 1000", fanout: 1000, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 34 | |
| 35 | // Invalid values - powers of 2 but less than 8 |
| 36 | {name: "invalid 1", fanout: 1, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 37 | {name: "invalid 2", fanout: 2, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 38 | {name: "invalid 4", fanout: 4, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 39 | |
| 40 | // Invalid values - exceeds 1024 |
| 41 | {name: "invalid 2048", fanout: 2048, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 42 | {name: "invalid 4096", fanout: 4096, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 43 | |
| 44 | // Invalid values - negative or zero |
| 45 | {name: "invalid 0", fanout: 0, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 46 | {name: "invalid -8", fanout: -8, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 47 | {name: "invalid -256", fanout: -256, wantErr: true, errMsg: "must be a power of 2, between 8 and 1024"}, |
| 48 | } |
| 49 | |
| 50 | for _, tt := range tests { |
| 51 | t.Run(tt.name, func(t *testing.T) { |
| 52 | cfg := &Import{ |
| 53 | UnixFSHAMTDirectoryMaxFanout: *NewOptionalInteger(tt.fanout), |
| 54 | } |
| 55 | |
| 56 | err := ValidateImportConfig(cfg) |
| 57 | |
| 58 | if tt.wantErr { |
| 59 | if err == nil { |
| 60 | t.Errorf("ValidateImportConfig() expected error for fanout=%d, got nil", tt.fanout) |
| 61 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 62 | t.Errorf("ValidateImportConfig() error = %v, want error containing %q", err, tt.errMsg) |
| 63 | } |
| 64 | } else { |
| 65 | if err != nil { |
| 66 | t.Errorf("ValidateImportConfig() unexpected error for fanout=%d: %v", tt.fanout, err) |
| 67 | } |
| 68 | } |
| 69 | }) |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | func TestValidateImportConfig_CidVersion(t *testing.T) { |
| 74 | tests := []struct { |
| 75 | name string |
| 76 | cidVer int64 |
| 77 | wantErr bool |
| 78 | errMsg string |
| 79 | }{ |
| 80 | {name: "valid 0", cidVer: 0, wantErr: false}, |
| 81 | {name: "valid 1", cidVer: 1, wantErr: false}, |
| 82 | {name: "invalid 2", cidVer: 2, wantErr: true, errMsg: "must be 0 or 1"}, |
| 83 | {name: "invalid -1", cidVer: -1, wantErr: true, errMsg: "must be 0 or 1"}, |
| 84 | {name: "invalid 100", cidVer: 100, wantErr: true, errMsg: "must be 0 or 1"}, |
| 85 | } |
| 86 | |
| 87 | for _, tt := range tests { |
| 88 | t.Run(tt.name, func(t *testing.T) { |
| 89 | cfg := &Import{ |
| 90 | CidVersion: *NewOptionalInteger(tt.cidVer), |
| 91 | } |
| 92 | |
| 93 | err := ValidateImportConfig(cfg) |
| 94 | |
| 95 | if tt.wantErr { |
| 96 | if err == nil { |
| 97 | t.Errorf("ValidateImportConfig() expected error for cidVer=%d, got nil", tt.cidVer) |
| 98 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 99 | t.Errorf("ValidateImportConfig() error = %v, want error containing %q", err, tt.errMsg) |
| 100 | } |
| 101 | } else { |
| 102 | if err != nil { |
| 103 | t.Errorf("ValidateImportConfig() unexpected error for cidVer=%d: %v", tt.cidVer, err) |
| 104 | } |
| 105 | } |
| 106 | }) |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | func TestValidateImportConfig_UnixFSFileMaxLinks(t *testing.T) { |
| 111 | tests := []struct { |
| 112 | name string |
| 113 | maxLinks int64 |
| 114 | wantErr bool |
| 115 | errMsg string |
| 116 | }{ |
| 117 | {name: "valid 1", maxLinks: 1, wantErr: false}, |
| 118 | {name: "valid 174", maxLinks: 174, wantErr: false}, |
| 119 | {name: "valid 1000", maxLinks: 1000, wantErr: false}, |
| 120 | {name: "invalid 0", maxLinks: 0, wantErr: true, errMsg: "must be positive"}, |
| 121 | {name: "invalid -1", maxLinks: -1, wantErr: true, errMsg: "must be positive"}, |
| 122 | } |
| 123 | |
| 124 | for _, tt := range tests { |
| 125 | t.Run(tt.name, func(t *testing.T) { |
| 126 | cfg := &Import{ |
| 127 | UnixFSFileMaxLinks: *NewOptionalInteger(tt.maxLinks), |
| 128 | } |
| 129 | |
| 130 | err := ValidateImportConfig(cfg) |
| 131 | |
| 132 | if tt.wantErr { |
| 133 | if err == nil { |
| 134 | t.Errorf("ValidateImportConfig() expected error for maxLinks=%d, got nil", tt.maxLinks) |
| 135 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 136 | t.Errorf("ValidateImportConfig() error = %v, want error containing %q", err, tt.errMsg) |
| 137 | } |
| 138 | } else { |
| 139 | if err != nil { |
| 140 | t.Errorf("ValidateImportConfig() unexpected error for maxLinks=%d: %v", tt.maxLinks, err) |
| 141 | } |
| 142 | } |
| 143 | }) |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | func TestValidateImportConfig_UnixFSDirectoryMaxLinks(t *testing.T) { |
| 148 | tests := []struct { |
| 149 | name string |
| 150 | maxLinks int64 |
| 151 | wantErr bool |
| 152 | errMsg string |
| 153 | }{ |
| 154 | {name: "valid 0", maxLinks: 0, wantErr: false}, // 0 means no limit |
| 155 | {name: "valid 1", maxLinks: 1, wantErr: false}, |
| 156 | {name: "valid 1000", maxLinks: 1000, wantErr: false}, |
| 157 | {name: "invalid -1", maxLinks: -1, wantErr: true, errMsg: "must be non-negative"}, |
| 158 | {name: "invalid -100", maxLinks: -100, wantErr: true, errMsg: "must be non-negative"}, |
| 159 | } |
| 160 | |
| 161 | for _, tt := range tests { |
| 162 | t.Run(tt.name, func(t *testing.T) { |
| 163 | cfg := &Import{ |
| 164 | UnixFSDirectoryMaxLinks: *NewOptionalInteger(tt.maxLinks), |
| 165 | } |
| 166 | |
| 167 | err := ValidateImportConfig(cfg) |
| 168 | |
| 169 | if tt.wantErr { |
| 170 | if err == nil { |
| 171 | t.Errorf("ValidateImportConfig() expected error for maxLinks=%d, got nil", tt.maxLinks) |
| 172 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 173 | t.Errorf("ValidateImportConfig() error = %v, want error containing %q", err, tt.errMsg) |
| 174 | } |
| 175 | } else { |
| 176 | if err != nil { |
| 177 | t.Errorf("ValidateImportConfig() unexpected error for maxLinks=%d: %v", tt.maxLinks, err) |
| 178 | } |
| 179 | } |
| 180 | }) |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | func TestValidateImportConfig_BatchMax(t *testing.T) { |
| 185 | tests := []struct { |
| 186 | name string |
| 187 | maxNodes int64 |
| 188 | maxSize int64 |
| 189 | wantErr bool |
| 190 | errMsg string |
| 191 | }{ |
| 192 | {name: "valid nodes 1", maxNodes: 1, maxSize: -999, wantErr: false}, |
| 193 | {name: "valid nodes 128", maxNodes: 128, maxSize: -999, wantErr: false}, |
| 194 | {name: "valid size 1", maxNodes: -999, maxSize: 1, wantErr: false}, |
| 195 | {name: "valid size 20MB", maxNodes: -999, maxSize: 20 << 20, wantErr: false}, |
| 196 | {name: "invalid nodes 0", maxNodes: 0, maxSize: -999, wantErr: true, errMsg: "BatchMaxNodes must be positive"}, |
| 197 | {name: "invalid nodes -1", maxNodes: -1, maxSize: -999, wantErr: true, errMsg: "BatchMaxNodes must be positive"}, |
| 198 | {name: "invalid size 0", maxNodes: -999, maxSize: 0, wantErr: true, errMsg: "BatchMaxSize must be positive"}, |
| 199 | {name: "invalid size -1", maxNodes: -999, maxSize: -1, wantErr: true, errMsg: "BatchMaxSize must be positive"}, |
| 200 | } |
| 201 | |
| 202 | for _, tt := range tests { |
| 203 | t.Run(tt.name, func(t *testing.T) { |
| 204 | cfg := &Import{} |
| 205 | if tt.maxNodes != -999 { |
| 206 | cfg.BatchMaxNodes = *NewOptionalInteger(tt.maxNodes) |
| 207 | } |
| 208 | if tt.maxSize != -999 { |
| 209 | cfg.BatchMaxSize = *NewOptionalInteger(tt.maxSize) |
| 210 | } |
| 211 | |
| 212 | err := ValidateImportConfig(cfg) |
| 213 | |
| 214 | if tt.wantErr { |
| 215 | if err == nil { |
| 216 | t.Errorf("ValidateImportConfig() expected error, got nil") |
| 217 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 218 | t.Errorf("ValidateImportConfig() error = %v, want error containing %q", err, tt.errMsg) |
| 219 | } |
| 220 | } else { |
| 221 | if err != nil { |
| 222 | t.Errorf("ValidateImportConfig() unexpected error: %v", err) |
| 223 | } |
| 224 | } |
| 225 | }) |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | func TestValidateImportConfig_UnixFSChunker(t *testing.T) { |
| 230 | tests := []struct { |
| 231 | name string |
| 232 | chunker string |
| 233 | wantErr bool |
| 234 | errMsg string |
| 235 | }{ |
| 236 | {name: "valid size-262144", chunker: "size-262144", wantErr: false}, |
| 237 | {name: "valid size-1", chunker: "size-1", wantErr: false}, |
| 238 | {name: "valid size-1048576", chunker: "size-1048576", wantErr: false}, |
| 239 | {name: "valid rabin", chunker: "rabin-128-256-512", wantErr: false}, |
| 240 | {name: "valid rabin min", chunker: "rabin-16-32-64", wantErr: false}, |
| 241 | {name: "valid buzhash", chunker: "buzhash", wantErr: false}, |
| 242 | {name: "invalid size-", chunker: "size-", wantErr: true, errMsg: "invalid format"}, |
| 243 | {name: "invalid size-abc", chunker: "size-abc", wantErr: true, errMsg: "invalid format"}, |
| 244 | {name: "invalid rabin-", chunker: "rabin-", wantErr: true, errMsg: "invalid format"}, |
| 245 | {name: "invalid rabin-128", chunker: "rabin-128", wantErr: true, errMsg: "invalid format"}, |
| 246 | {name: "invalid rabin-128-256", chunker: "rabin-128-256", wantErr: true, errMsg: "invalid format"}, |
| 247 | {name: "invalid rabin-a-b-c", chunker: "rabin-a-b-c", wantErr: true, errMsg: "invalid format"}, |
| 248 | {name: "invalid unknown", chunker: "unknown", wantErr: true, errMsg: "invalid format"}, |
| 249 | {name: "invalid empty", chunker: "", wantErr: true, errMsg: "invalid format"}, |
| 250 | } |
| 251 | |
| 252 | for _, tt := range tests { |
| 253 | t.Run(tt.name, func(t *testing.T) { |
| 254 | cfg := &Import{ |
| 255 | UnixFSChunker: *NewOptionalString(tt.chunker), |
| 256 | } |
| 257 | |
| 258 | err := ValidateImportConfig(cfg) |
| 259 | |
| 260 | if tt.wantErr { |
| 261 | if err == nil { |
| 262 | t.Errorf("ValidateImportConfig() expected error for chunker=%s, got nil", tt.chunker) |
| 263 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 264 | t.Errorf("ValidateImportConfig() error = %v, want error containing %q", err, tt.errMsg) |
| 265 | } |
| 266 | } else { |
| 267 | if err != nil { |
| 268 | t.Errorf("ValidateImportConfig() unexpected error for chunker=%s: %v", tt.chunker, err) |
| 269 | } |
| 270 | } |
| 271 | }) |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | func TestValidateImportConfig_HashFunction(t *testing.T) { |
| 276 | tests := []struct { |
| 277 | name string |
| 278 | hashFunc string |
| 279 | wantErr bool |
| 280 | errMsg string |
| 281 | }{ |
| 282 | {name: "valid sha2-256", hashFunc: "sha2-256", wantErr: false}, |
| 283 | {name: "valid sha2-512", hashFunc: "sha2-512", wantErr: false}, |
| 284 | {name: "valid sha3-256", hashFunc: "sha3-256", wantErr: false}, |
| 285 | {name: "valid blake2b-256", hashFunc: "blake2b-256", wantErr: false}, |
| 286 | {name: "valid blake3", hashFunc: "blake3", wantErr: false}, |
| 287 | {name: "invalid unknown", hashFunc: "unknown-hash", wantErr: true, errMsg: "unrecognized"}, |
| 288 | {name: "invalid empty", hashFunc: "", wantErr: true, errMsg: "unrecognized"}, |
| 289 | } |
| 290 | |
| 291 | // Check for hashes that exist but are not allowed |
| 292 | // MD5 should exist but not be allowed |
| 293 | if code, ok := mh.Names["md5"]; ok { |
| 294 | tests = append(tests, struct { |
| 295 | name string |
| 296 | hashFunc string |
| 297 | wantErr bool |
| 298 | errMsg string |
| 299 | }{name: "md5 not allowed", hashFunc: "md5", wantErr: true, errMsg: "not allowed"}) |
| 300 | _ = code // use the variable |
| 301 | } |
| 302 | |
| 303 | for _, tt := range tests { |
| 304 | t.Run(tt.name, func(t *testing.T) { |
| 305 | cfg := &Import{ |
| 306 | HashFunction: *NewOptionalString(tt.hashFunc), |
| 307 | } |
| 308 | |
| 309 | err := ValidateImportConfig(cfg) |
| 310 | |
| 311 | if tt.wantErr { |
| 312 | if err == nil { |
| 313 | t.Errorf("ValidateImportConfig() expected error for hashFunc=%s, got nil", tt.hashFunc) |
| 314 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 315 | t.Errorf("ValidateImportConfig() error = %v, want error containing %q", err, tt.errMsg) |
| 316 | } |
| 317 | } else { |
| 318 | if err != nil { |
| 319 | t.Errorf("ValidateImportConfig() unexpected error for hashFunc=%s: %v", tt.hashFunc, err) |
| 320 | } |
| 321 | } |
| 322 | }) |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | func TestValidateImportConfig_DefaultValue(t *testing.T) { |
| 327 | // Test that default (unset) value doesn't trigger validation |
| 328 | cfg := &Import{} |
| 329 | |
| 330 | err := ValidateImportConfig(cfg) |
| 331 | if err != nil { |
| 332 | t.Errorf("ValidateImportConfig() unexpected error for default config: %v", err) |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | func TestIsValidChunker(t *testing.T) { |
| 337 | tests := []struct { |
| 338 | chunker string |
| 339 | want bool |
| 340 | }{ |
| 341 | {"buzhash", true}, |
| 342 | {"size-262144", true}, |
| 343 | {"size-1", true}, |
| 344 | {"size-0", false}, // 0 is not valid - must be positive |
| 345 | {"size-9999999", true}, |
| 346 | {"rabin-128-256-512", true}, |
| 347 | {"rabin-16-32-64", true}, |
| 348 | {"rabin-1-2-3", true}, |
| 349 | {"rabin-512-256-128", false}, // Invalid ordering: min > avg > max |
| 350 | {"rabin-256-128-512", false}, // Invalid ordering: min > avg |
| 351 | {"rabin-128-512-256", false}, // Invalid ordering: avg > max |
| 352 | |
| 353 | {"", false}, |
| 354 | {"size-", false}, |
| 355 | {"size-abc", false}, |
| 356 | {"size--1", false}, |
| 357 | {"rabin-", false}, |
| 358 | {"rabin-128", false}, |
| 359 | {"rabin-128-256", false}, |
| 360 | {"rabin-128-256-512-1024", false}, |
| 361 | {"rabin-a-b-c", false}, |
| 362 | {"unknown", false}, |
| 363 | {"buzzhash", false}, // typo |
| 364 | } |
| 365 | |
| 366 | for _, tt := range tests { |
| 367 | t.Run(tt.chunker, func(t *testing.T) { |
| 368 | if got := isValidChunker(tt.chunker); got != tt.want { |
| 369 | t.Errorf("isValidChunker(%q) = %v, want %v", tt.chunker, got, tt.want) |
| 370 | } |
| 371 | }) |
| 372 | } |
| 373 | } |
| 374 | |
| 375 | func TestIsPowerOfTwo(t *testing.T) { |
| 376 | tests := []struct { |
| 377 | n int64 |
| 378 | want bool |
| 379 | }{ |
| 380 | {0, false}, |
| 381 | {1, true}, |
| 382 | {2, true}, |
| 383 | {3, false}, |
| 384 | {4, true}, |
| 385 | {5, false}, |
| 386 | {6, false}, |
| 387 | {7, false}, |
| 388 | {8, true}, |
| 389 | {16, true}, |
| 390 | {32, true}, |
| 391 | {64, true}, |
| 392 | {100, false}, |
| 393 | {128, true}, |
| 394 | {256, true}, |
| 395 | {512, true}, |
| 396 | {1024, true}, |
| 397 | {2048, true}, |
| 398 | {-1, false}, |
| 399 | {-8, false}, |
| 400 | } |
| 401 | |
| 402 | for _, tt := range tests { |
| 403 | t.Run("", func(t *testing.T) { |
| 404 | if got := isPowerOfTwo(tt.n); got != tt.want { |
| 405 | t.Errorf("isPowerOfTwo(%d) = %v, want %v", tt.n, got, tt.want) |
| 406 | } |
| 407 | }) |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | func TestValidateImportConfig_HAMTSizeEstimation(t *testing.T) { |
| 412 | tests := []struct { |
| 413 | name string |
| 414 | value string |
| 415 | wantErr bool |
| 416 | errMsg string |
| 417 | }{ |
| 418 | {name: "valid links", value: HAMTSizeEstimationLinks, wantErr: false}, |
| 419 | {name: "valid block", value: HAMTSizeEstimationBlock, wantErr: false}, |
| 420 | {name: "valid disabled", value: HAMTSizeEstimationDisabled, wantErr: false}, |
| 421 | {name: "invalid unknown", value: "unknown", wantErr: true, errMsg: "must be"}, |
| 422 | {name: "invalid empty", value: "", wantErr: true, errMsg: "must be"}, |
| 423 | {name: "invalid typo", value: "link", wantErr: true, errMsg: "must be"}, |
| 424 | } |
| 425 | |
| 426 | for _, tt := range tests { |
| 427 | t.Run(tt.name, func(t *testing.T) { |
| 428 | cfg := &Import{ |
| 429 | UnixFSHAMTDirectorySizeEstimation: *NewOptionalString(tt.value), |
| 430 | } |
| 431 | |
| 432 | err := ValidateImportConfig(cfg) |
| 433 | |
| 434 | if tt.wantErr { |
| 435 | if err == nil { |
| 436 | t.Errorf("expected error for value=%q, got nil", tt.value) |
| 437 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 438 | t.Errorf("error = %v, want error containing %q", err, tt.errMsg) |
| 439 | } |
| 440 | } else { |
| 441 | if err != nil { |
| 442 | t.Errorf("unexpected error for value=%q: %v", tt.value, err) |
| 443 | } |
| 444 | } |
| 445 | }) |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | func TestValidateImportConfig_DAGLayout(t *testing.T) { |
| 450 | tests := []struct { |
| 451 | name string |
| 452 | value string |
| 453 | wantErr bool |
| 454 | errMsg string |
| 455 | }{ |
| 456 | {name: "valid balanced", value: DAGLayoutBalanced, wantErr: false}, |
| 457 | {name: "valid trickle", value: DAGLayoutTrickle, wantErr: false}, |
| 458 | {name: "invalid unknown", value: "unknown", wantErr: true, errMsg: "must be"}, |
| 459 | {name: "invalid empty", value: "", wantErr: true, errMsg: "must be"}, |
| 460 | {name: "invalid flat", value: "flat", wantErr: true, errMsg: "must be"}, |
| 461 | } |
| 462 | |
| 463 | for _, tt := range tests { |
| 464 | t.Run(tt.name, func(t *testing.T) { |
| 465 | cfg := &Import{ |
| 466 | UnixFSDAGLayout: *NewOptionalString(tt.value), |
| 467 | } |
| 468 | |
| 469 | err := ValidateImportConfig(cfg) |
| 470 | |
| 471 | if tt.wantErr { |
| 472 | if err == nil { |
| 473 | t.Errorf("expected error for value=%q, got nil", tt.value) |
| 474 | } else if tt.errMsg != "" && !strings.Contains(err.Error(), tt.errMsg) { |
| 475 | t.Errorf("error = %v, want error containing %q", err, tt.errMsg) |
| 476 | } |
| 477 | } else { |
| 478 | if err != nil { |
| 479 | t.Errorf("unexpected error for value=%q: %v", tt.value, err) |
| 480 | } |
| 481 | } |
| 482 | }) |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | func TestImport_UnixFSCidBuilder(t *testing.T) { |
| 487 | defaultMhType := mh.Names[strings.ToLower(DefaultHashFunction)] |
| 488 | |
| 489 | tests := []struct { |
| 490 | name string |
| 491 | cfg Import |
| 492 | wantCidVer uint64 |
| 493 | wantMhType uint64 |
| 494 | }{ |
| 495 | { |
| 496 | name: "CIDv1 explicit", |
| 497 | cfg: Import{CidVersion: *NewOptionalInteger(1)}, |
| 498 | wantCidVer: 1, |
| 499 | wantMhType: defaultMhType, |
| 500 | }, |
| 501 | { |
| 502 | name: "CIDv0 explicit", |
| 503 | cfg: Import{CidVersion: *NewOptionalInteger(0)}, |
| 504 | wantCidVer: 0, |
| 505 | wantMhType: defaultMhType, |
| 506 | }, |
| 507 | { |
| 508 | name: "non-default hash upgrades CIDv0 to CIDv1", |
| 509 | cfg: Import{HashFunction: *NewOptionalString("sha2-512")}, |
| 510 | wantCidVer: 1, |
| 511 | wantMhType: mh.SHA2_512, |
| 512 | }, |
| 513 | { |
| 514 | name: "CIDv1 with sha2-512", |
| 515 | cfg: Import{ |
| 516 | CidVersion: *NewOptionalInteger(1), |
| 517 | HashFunction: *NewOptionalString("sha2-512"), |
| 518 | }, |
| 519 | wantCidVer: 1, |
| 520 | wantMhType: mh.SHA2_512, |
| 521 | }, |
| 522 | } |
| 523 | |
| 524 | for _, tt := range tests { |
| 525 | t.Run(tt.name, func(t *testing.T) { |
| 526 | builder, err := tt.cfg.UnixFSCidBuilder() |
| 527 | if err != nil { |
| 528 | t.Fatalf("unexpected error: %v", err) |
| 529 | } |
| 530 | if builder == nil { |
| 531 | t.Fatal("expected non-nil builder") |
| 532 | } |
| 533 | c, err := builder.Sum([]byte("test")) |
| 534 | if err != nil { |
| 535 | t.Fatalf("builder.Sum failed: %v", err) |
| 536 | } |
| 537 | pref := c.Prefix() |
| 538 | if pref.Version != tt.wantCidVer { |
| 539 | t.Errorf("CID version = %d, want %d", pref.Version, tt.wantCidVer) |
| 540 | } |
| 541 | if pref.MhType != tt.wantMhType { |
| 542 | t.Errorf("multihash type = 0x%x, want 0x%x", pref.MhType, tt.wantMhType) |
| 543 | } |
| 544 | }) |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | // TestImport_UnixFSCidBuilderDefaults verifies that UnixFSCidBuilder always |
| 549 | // returns an explicit builder even when no config is set, so that MFS |
| 550 | // respects kubo's DefaultCidVersion rather than relying on boxo's internal |
| 551 | // CIDv0 default (relevant for https://github.com/ipfs/kubo/issues/4143). |
| 552 | func TestImport_UnixFSCidBuilderDefaults(t *testing.T) { |
| 553 | cfg := &Import{} |
| 554 | builder, err := cfg.UnixFSCidBuilder() |
| 555 | if err != nil { |
| 556 | t.Fatalf("unexpected error: %v", err) |
| 557 | } |
| 558 | if builder == nil { |
| 559 | t.Fatal("expected non-nil builder at defaults") |
| 560 | } |
| 561 | c, err := builder.Sum([]byte("test")) |
| 562 | if err != nil { |
| 563 | t.Fatalf("builder.Sum failed: %v", err) |
| 564 | } |
| 565 | pref := c.Prefix() |
| 566 | if pref.Version != uint64(DefaultCidVersion) { |
| 567 | t.Errorf("CID version = %d, want DefaultCidVersion (%d)", pref.Version, DefaultCidVersion) |
| 568 | } |
| 569 | wantMhType := mh.Names[strings.ToLower(DefaultHashFunction)] |
| 570 | if pref.MhType != wantMhType { |
| 571 | t.Errorf("multihash type = 0x%x, want 0x%x (DefaultHashFunction=%s)", pref.MhType, wantMhType, DefaultHashFunction) |
| 572 | } |
| 573 | } |
| 574 | |
| 575 | func TestImport_HAMTSizeEstimationMode(t *testing.T) { |
| 576 | tests := []struct { |
| 577 | cfg string |
| 578 | want io.SizeEstimationMode |
| 579 | }{ |
| 580 | {HAMTSizeEstimationLinks, io.SizeEstimationLinks}, |
| 581 | {HAMTSizeEstimationBlock, io.SizeEstimationBlock}, |
| 582 | {HAMTSizeEstimationDisabled, io.SizeEstimationDisabled}, |
| 583 | {"", io.SizeEstimationLinks}, // default (unset returns default) |
| 584 | {"unknown", io.SizeEstimationLinks}, // fallback to default |
| 585 | } |
| 586 | |
| 587 | for _, tt := range tests { |
| 588 | t.Run(tt.cfg, func(t *testing.T) { |
| 589 | var imp Import |
| 590 | if tt.cfg != "" { |
| 591 | imp.UnixFSHAMTDirectorySizeEstimation = *NewOptionalString(tt.cfg) |
| 592 | } |
| 593 | got := imp.HAMTSizeEstimationMode() |
| 594 | if got != tt.want { |
| 595 | t.Errorf("Import.HAMTSizeEstimationMode() with %q = %v, want %v", tt.cfg, got, tt.want) |
| 596 | } |
| 597 | }) |
| 598 | } |
| 599 | } |