serverlist_parser_test.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package pia
  2. import (
  3. "os"
  4. "path/filepath"
  5. "testing"
  6. )
  7. func TestServerListAdapters(t *testing.T) {
  8. tests := []struct {
  9. file, hint, schema, id, hostname string
  10. }{
  11. {"v6_valid.json", "6", "v6", "us-east", "useast401"},
  12. {"v7_valid.json", "7", "v7", "de-berlin", "berlin501"},
  13. }
  14. for _, test := range tests {
  15. raw, err := os.ReadFile(filepath.Join("testdata", "serverlist", test.file))
  16. if err != nil {
  17. t.Fatal(err)
  18. }
  19. regions, schema, err := ParseServerList(raw, test.hint)
  20. if err != nil {
  21. t.Fatalf("%s: %v", test.file, err)
  22. }
  23. if schema != test.schema || len(regions) != 1 || regions[0].ID != test.id || regions[0].WireGuard[0].Hostname != test.hostname {
  24. t.Fatalf("unexpected parsed result for %s: schema=%s regions=%+v", test.file, schema, regions)
  25. }
  26. }
  27. raw, err := os.ReadFile(filepath.Join("testdata", "serverlist", "v7_valid.json"))
  28. if err != nil {
  29. t.Fatal(err)
  30. }
  31. regions, schema, err := ParseServerList(raw, "6")
  32. if err != nil || schema != "v7" || regions[0].ID != "de-berlin" {
  33. t.Fatalf("detected schema did not override a stale endpoint hint: schema=%q regions=%v err=%v", schema, regions, err)
  34. }
  35. legacy := []byte(`{"groups":{"wg":[]},"regions":[{"id":"legacy","name":"Legacy","country":"US","geo":false,"offline":false,"servers":{"wg":[{"ip":"198.51.100.9","cn":"legacy.example"}]}}]}`)
  36. regions, schema, err = ParseServerList(legacy, "")
  37. if err != nil || schema != "v6" || regions[0].ID != "legacy" {
  38. t.Fatalf("versionless v6 fallback failed: schema=%q regions=%v err=%v", schema, regions, err)
  39. }
  40. }
  41. func TestServerListRejectsMalformedFields(t *testing.T) {
  42. raw, err := os.ReadFile(filepath.Join("testdata", "serverlist", "malformed.json"))
  43. if err != nil {
  44. t.Fatal(err)
  45. }
  46. if _, _, err := ParseServerList(raw, "6"); err == nil || CodeOf(err) != CodeCatalogSchemaUnsupported {
  47. t.Fatalf("expected %s for malformed server list, got %s: %v", CodeCatalogSchemaUnsupported, CodeOf(err), err)
  48. }
  49. }
  50. func TestServerListRejectsUnsupportedDuplicateAndTrailingData(t *testing.T) {
  51. tests := []struct {
  52. name, raw, hint string
  53. }{
  54. {"unsupported schema", `{"version":99,"groups":{},"regions":[]}`, ""},
  55. {"invalid version value", `{"version":"v7beta","groups":{"wg":[]},"regions":[]}`, ""},
  56. {"trailing JSON", `{"version":6,"groups":{},"regions":[]} {}`, "6"},
  57. {"wrong groups type", `{"version":6,"groups":[],"regions":[]}`, "6"},
  58. {"wrong field type", `{"version":6,"groups":{"wg":[]},"regions":[{"id":7,"name":"One","country":"US","geo":false,"offline":false,"servers":{"wg":[]}}]}`, "6"},
  59. }
  60. for _, test := range tests {
  61. t.Run(test.name, func(t *testing.T) {
  62. if _, _, err := ParseServerList([]byte(test.raw), test.hint); err == nil || CodeOf(err) != CodeCatalogSchemaUnsupported {
  63. t.Fatalf("expected %s, got %s: %v", CodeCatalogSchemaUnsupported, CodeOf(err), err)
  64. }
  65. })
  66. }
  67. }
  68. func TestServerListSkipsBadRows(t *testing.T) {
  69. duplicate := []byte(`{"version":6,"groups":{"wg":[]},"regions":[{"id":"same","name":"One","country":"US","geo":false,"offline":false,"servers":{"wg":[{"ip":"198.51.100.1","cn":"one.example"}]}},{"id":"SAME","name":"Two","country":"US","geo":false,"offline":false,"servers":{"wg":[{"ip":"198.51.100.2","cn":"two.example"}]}}]}`)
  70. regions, _, err := ParseServerList(duplicate, "6")
  71. if err != nil || len(regions) != 1 || regions[0].ID != "same" || regions[0].WireGuard[0].Hostname != "one.example" {
  72. t.Fatalf("duplicate region id should keep the first: regions=%+v err=%v", regions, err)
  73. }
  74. mixed := []byte(`{"version":6,"groups":{"wg":[]},"regions":[{"id":"us-east","name":"US East","country":"US","geo":false,"offline":false,"servers":{"wg":[{"ip":"2001:db8::1","cn":"bad6"},{"ip":"198.51.100.10","cn":"useast1"}]}}]}`)
  75. regions, _, err = ParseServerList(mixed, "6")
  76. if err != nil || len(regions) != 1 || len(regions[0].WireGuard) != 1 || regions[0].WireGuard[0].Hostname != "useast1" {
  77. t.Fatalf("invalid WireGuard server should be skipped: regions=%+v err=%v", regions, err)
  78. }
  79. }
  80. func FuzzParseServerList(f *testing.F) {
  81. for _, name := range []string{"v6_valid.json", "v7_valid.json", "malformed.json"} {
  82. raw, err := os.ReadFile(filepath.Join("testdata", "serverlist", name))
  83. if err != nil {
  84. f.Fatal(err)
  85. }
  86. f.Add(raw)
  87. }
  88. f.Fuzz(func(t *testing.T, raw []byte) {
  89. _, _, _ = ParseServerList(raw, "6")
  90. })
  91. }