master
go 209 lines 6.39 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package main
4
5 import (
6 "archive/zip"
7 "bytes"
8 "compress/gzip"
9 "fmt"
10 "net/http"
11 "net/http/httptest"
12 "testing"
13 "time"
14
15 "github.com/stretchr/testify/require"
16 )
17
18 func TestDecodePayloadAutoGzip(t *testing.T) {
19 var compressed bytes.Buffer
20 zw := gzip.NewWriter(&compressed)
21 _, err := zw.Write([]byte("hello"))
22 require.NoError(t, err)
23 require.NoError(t, zw.Close())
24
25 decoded, err := decodePayload(compressed.Bytes())
26 require.NoError(t, err)
27 require.Equal(t, []byte("hello"), decoded)
28 }
29
30 func TestDecodePayloadAutoZip(t *testing.T) {
31 var zipped bytes.Buffer
32 zw := zip.NewWriter(&zipped)
33 entry, err := zw.Create("data.txt")
34 require.NoError(t, err)
35 _, err = entry.Write([]byte("world"))
36 require.NoError(t, err)
37 require.NoError(t, zw.Close())
38
39 decoded, err := decodePayload(zipped.Bytes())
40 require.NoError(t, err)
41 require.Equal(t, []byte("world"), decoded)
42 }
43
44 func TestResolveDBIPArtifactURL(t *testing.T) {
45 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46 require.Equal(t, "/download", r.URL.Path)
47 _, _ = w.Write([]byte(`
48 <html>
49 <body>
50 <a href="https://download.db-ip.com/free/dbip-city-lite-2026-03.mmdb.gz">MMDB</a>
51 <a href="https://download.db-ip.com/free/dbip-city-lite-2026-03.csv.gz">CSV</a>
52 </body>
53 </html>
54 `))
55 }))
56 defer server.Close()
57
58 dl := newDownloader(httpConfig{timeout: time.Second, userAgent: "test"})
59 resolved, err := dl.resolveDBIPArtifactURL(server.URL+"/download", artifactDBIPCityLite, formatMMDB)
60 require.NoError(t, err)
61 require.Equal(t, "https://download.db-ip.com/free/dbip-city-lite-2026-03.mmdb.gz", resolved)
62 }
63
64 func TestResolveCAIDAPrefix2ASURL(t *testing.T) {
65 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
66 require.Equal(t, "/routing/pfx2as-creation.log", r.URL.Path)
67 _, _ = w.Write([]byte(
68 "1\t1778084001\t2026/05/routeviews-rv2-20260505-1200.pfx2as.gz\n" +
69 "2\t1778170446\t2026/05/routeviews-rv2-20260506-1200.pfx2as.gz\n" +
70 "3\t1777997562\t2026/05/routeviews-rv2-20260504-1200.pfx2as.gz\n",
71 ))
72 }))
73 defer server.Close()
74
75 dl := newDownloader(httpConfig{timeout: time.Second, userAgent: "test"})
76 resolved, err := dl.resolveCAIDAPrefix2ASURL(server.URL + "/routing/pfx2as-creation.log?mirror=local")
77 require.NoError(t, err)
78 require.Equal(t, server.URL+"/routing/2026/05/routeviews-rv2-20260506-1200.pfx2as.gz", resolved)
79 }
80
81 func TestResolveMaxMindSourceExpandsEnvForFetchAndRedactsMetadata(t *testing.T) {
82 t.Setenv("MAXMIND_LICENSE_KEY", "secret-license-key")
83
84 dl := newDownloader(httpConfig{timeout: time.Second, userAgent: "test"})
85 resolved, err := dl.resolveSource(sourceEntry{
86 name: "maxmind-asn",
87 family: sourceFamilyASN,
88 provider: providerMaxMind,
89 artifact: artifactMaxMindGeoLite2ASN,
90 format: formatMMDB,
91 })
92 require.NoError(t, err)
93 require.Contains(t, resolved.fetchURL, "secret-license-key")
94 require.NotContains(t, resolved.ref.URL, "secret-license-key")
95 require.Contains(t, resolved.ref.URL, "redacted")
96 }
97
98 func TestResolveMaxMindSourceReportsMissingEnvWithoutSecretURL(t *testing.T) {
99 t.Setenv("MAXMIND_LICENSE_KEY", "")
100
101 dl := newDownloader(httpConfig{timeout: time.Second, userAgent: "test"})
102 _, err := dl.resolveSource(sourceEntry{
103 name: "maxmind-asn",
104 family: sourceFamilyASN,
105 provider: providerMaxMind,
106 artifact: artifactMaxMindGeoLite2ASN,
107 format: formatMMDB,
108 })
109 require.Error(t, err)
110 require.Contains(t, err.Error(), "MAXMIND_LICENSE_KEY")
111 }
112
113 func TestDecodeMaxMindASNPayloadExtractsTarredMMDB(t *testing.T) {
114 payload := "fake mmdb"
115 tarball := buildTarGZ(t, map[string]string{
116 "GeoLite2-ASN_20260508/GeoLite2-ASN.mmdb": payload,
117 })
118
119 decoded, err := decodeMaxMindASNPayload(tarball)
120 require.NoError(t, err)
121 require.Equal(t, []byte(payload), decoded)
122 }
123
124 func TestDecodeMaxMindASNPayloadExtractsTarWithoutUSTARMagic(t *testing.T) {
125 payload := "legacy tar mmdb"
126 rawTar := buildTar(t, map[string]string{
127 "GeoLite2-ASN_20260508/GeoLite2-ASN.mmdb": payload,
128 })
129 clearTarMagicAndRecomputeChecksum(t, rawTar[:512])
130
131 var compressed bytes.Buffer
132 zw := gzip.NewWriter(&compressed)
133 _, err := zw.Write(rawTar)
134 require.NoError(t, err)
135 require.NoError(t, zw.Close())
136
137 decoded, err := decodeMaxMindASNPayload(compressed.Bytes())
138 require.NoError(t, err)
139 require.Equal(t, []byte(payload), decoded)
140 }
141
142 func TestDecodeMaxMindASNPayloadAcceptsGzippedMMDB(t *testing.T) {
143 payload := []byte("fake mmdb")
144 var compressed bytes.Buffer
145 zw := gzip.NewWriter(&compressed)
146 _, err := zw.Write(payload)
147 require.NoError(t, err)
148 require.NoError(t, zw.Close())
149
150 decoded, err := decodeMaxMindASNPayload(compressed.Bytes())
151 require.NoError(t, err)
152 require.Equal(t, payload, decoded)
153 }
154
155 func TestDecodeMaxMindASNPayloadRejectsCorruptTarPayload(t *testing.T) {
156 tarLikeCorruptPayload := make([]byte, 512)
157 copy(tarLikeCorruptPayload[257:], []byte("ustar"))
158
159 var compressed bytes.Buffer
160 zw := gzip.NewWriter(&compressed)
161 _, err := zw.Write(tarLikeCorruptPayload)
162 require.NoError(t, err)
163 require.NoError(t, zw.Close())
164
165 _, err = decodeMaxMindASNPayload(compressed.Bytes())
166 require.Error(t, err)
167 require.Contains(t, err.Error(), "failed to extract MaxMind ASN MMDB from tar payload")
168 }
169
170 func TestExpandEnvPlaceholdersRequiresMissingVariable(t *testing.T) {
171 t.Setenv("TOPOLOGY_TEST_PRESENT", "value")
172 _, err := expandEnvPlaceholders("https://example.test/${TOPOLOGY_TEST_MISSING}/${TOPOLOGY_TEST_PRESENT}")
173 require.Error(t, err)
174 require.Contains(t, err.Error(), "TOPOLOGY_TEST_MISSING")
175 require.NotContains(t, err.Error(), "value")
176 }
177
178 func TestResolveSourceUsesLocalPathWithoutNetwork(t *testing.T) {
179 dl := newDownloader(httpConfig{timeout: time.Second, userAgent: "test"})
180 resolved, err := dl.resolveSource(sourceEntry{
181 name: "custom-city",
182 family: sourceFamilyGeo,
183 provider: providerDBIP,
184 artifact: artifactDBIPCityLite,
185 format: formatMMDB,
186 path: "/tmp/custom-city.mmdb",
187 })
188 require.NoError(t, err)
189 require.Equal(t, "/tmp/custom-city.mmdb", resolved.fetchPath)
190 require.Equal(t, "path", resolved.ref.Source)
191 require.Equal(t, "/tmp/custom-city.mmdb", resolved.ref.Path)
192 }
193
194 func clearTarMagicAndRecomputeChecksum(t *testing.T, header []byte) {
195 t.Helper()
196
197 require.Len(t, header, 512)
198 for i := 257; i < 265; i++ {
199 header[i] = 0
200 }
201 for i := 148; i < 156; i++ {
202 header[i] = ' '
203 }
204 sum := 0
205 for _, b := range header {
206 sum += int(b)
207 }
208 copy(header[148:156], fmt.Appendf(nil, "%06o\x00 ", sum))
209 }