code.gitea.io/gitea@v1.21.7/tests/integration/api_packages_rpm_test.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package integration 5 6 import ( 7 "bytes" 8 "compress/gzip" 9 "encoding/base64" 10 "encoding/xml" 11 "fmt" 12 "io" 13 "net/http" 14 "net/http/httptest" 15 "testing" 16 17 "code.gitea.io/gitea/models/db" 18 "code.gitea.io/gitea/models/packages" 19 "code.gitea.io/gitea/models/unittest" 20 user_model "code.gitea.io/gitea/models/user" 21 rpm_module "code.gitea.io/gitea/modules/packages/rpm" 22 "code.gitea.io/gitea/modules/setting" 23 "code.gitea.io/gitea/tests" 24 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func TestPackageRpm(t *testing.T) { 29 defer tests.PrepareTestEnv(t)() 30 31 packageName := "gitea-test" 32 packageVersion := "1.0.2-1" 33 packageArchitecture := "x86_64" 34 35 user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 36 37 base64RpmPackageContent := `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` 65 rpmPackageContent, err := base64.StdEncoding.DecodeString(base64RpmPackageContent) 66 assert.NoError(t, err) 67 68 zr, err := gzip.NewReader(bytes.NewReader(rpmPackageContent)) 69 assert.NoError(t, err) 70 71 content, err := io.ReadAll(zr) 72 assert.NoError(t, err) 73 74 rootURL := fmt.Sprintf("/api/packages/%s/rpm", user.Name) 75 76 t.Run("RepositoryConfig", func(t *testing.T) { 77 defer tests.PrintCurrentTest(t)() 78 79 req := NewRequest(t, "GET", rootURL+".repo") 80 resp := MakeRequest(t, req, http.StatusOK) 81 82 expected := fmt.Sprintf(`[gitea-%s] 83 name=%s - %s 84 baseurl=%sapi/packages/%s/rpm 85 enabled=1 86 gpgcheck=1 87 gpgkey=%sapi/packages/%s/rpm/repository.key`, user.Name, user.Name, setting.AppName, setting.AppURL, user.Name, setting.AppURL, user.Name) 88 89 assert.Equal(t, expected, resp.Body.String()) 90 }) 91 92 t.Run("RepositoryKey", func(t *testing.T) { 93 defer tests.PrintCurrentTest(t)() 94 95 req := NewRequest(t, "GET", rootURL+"/repository.key") 96 resp := MakeRequest(t, req, http.StatusOK) 97 98 assert.Equal(t, "application/pgp-keys", resp.Header().Get("Content-Type")) 99 assert.Contains(t, resp.Body.String(), "-----BEGIN PGP PUBLIC KEY BLOCK-----") 100 }) 101 102 t.Run("Upload", func(t *testing.T) { 103 url := rootURL + "/upload" 104 105 req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)) 106 MakeRequest(t, req, http.StatusUnauthorized) 107 108 req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)) 109 req = AddBasicAuthHeader(req, user.Name) 110 MakeRequest(t, req, http.StatusCreated) 111 112 pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRpm) 113 assert.NoError(t, err) 114 assert.Len(t, pvs, 1) 115 116 pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) 117 assert.NoError(t, err) 118 assert.Nil(t, pd.SemVer) 119 assert.IsType(t, &rpm_module.VersionMetadata{}, pd.Metadata) 120 assert.Equal(t, packageName, pd.Package.Name) 121 assert.Equal(t, packageVersion, pd.Version.Version) 122 123 pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) 124 assert.NoError(t, err) 125 assert.Len(t, pfs, 1) 126 assert.Equal(t, fmt.Sprintf("%s-%s.%s.rpm", packageName, packageVersion, packageArchitecture), pfs[0].Name) 127 assert.True(t, pfs[0].IsLead) 128 129 pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID) 130 assert.NoError(t, err) 131 assert.Equal(t, int64(len(content)), pb.Size) 132 133 req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)) 134 req = AddBasicAuthHeader(req, user.Name) 135 MakeRequest(t, req, http.StatusConflict) 136 }) 137 138 t.Run("Download", func(t *testing.T) { 139 defer tests.PrintCurrentTest(t)() 140 141 req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)) 142 resp := MakeRequest(t, req, http.StatusOK) 143 144 assert.Equal(t, content, resp.Body.Bytes()) 145 }) 146 147 t.Run("Repository", func(t *testing.T) { 148 defer tests.PrintCurrentTest(t)() 149 150 url := rootURL + "/repodata" 151 152 req := NewRequest(t, "HEAD", url+"/dummy.xml") 153 MakeRequest(t, req, http.StatusNotFound) 154 155 req = NewRequest(t, "GET", url+"/dummy.xml") 156 MakeRequest(t, req, http.StatusNotFound) 157 158 t.Run("repomd.xml", func(t *testing.T) { 159 defer tests.PrintCurrentTest(t)() 160 161 req = NewRequest(t, "HEAD", url+"/repomd.xml") 162 MakeRequest(t, req, http.StatusOK) 163 164 req = NewRequest(t, "GET", url+"/repomd.xml") 165 resp := MakeRequest(t, req, http.StatusOK) 166 167 type Repomd struct { 168 XMLName xml.Name `xml:"repomd"` 169 Xmlns string `xml:"xmlns,attr"` 170 XmlnsRpm string `xml:"xmlns:rpm,attr"` 171 Data []struct { 172 Type string `xml:"type,attr"` 173 Checksum struct { 174 Value string `xml:",chardata"` 175 Type string `xml:"type,attr"` 176 } `xml:"checksum"` 177 OpenChecksum struct { 178 Value string `xml:",chardata"` 179 Type string `xml:"type,attr"` 180 } `xml:"open-checksum"` 181 Location struct { 182 Href string `xml:"href,attr"` 183 } `xml:"location"` 184 Timestamp int64 `xml:"timestamp"` 185 Size int64 `xml:"size"` 186 OpenSize int64 `xml:"open-size"` 187 } `xml:"data"` 188 } 189 190 var result Repomd 191 decodeXML(t, resp, &result) 192 193 assert.Len(t, result.Data, 3) 194 for _, d := range result.Data { 195 assert.Equal(t, "sha256", d.Checksum.Type) 196 assert.NotEmpty(t, d.Checksum.Value) 197 assert.Equal(t, "sha256", d.OpenChecksum.Type) 198 assert.NotEmpty(t, d.OpenChecksum.Value) 199 assert.NotEqual(t, d.Checksum.Value, d.OpenChecksum.Value) 200 assert.Greater(t, d.OpenSize, d.Size) 201 202 switch d.Type { 203 case "primary": 204 assert.EqualValues(t, 718, d.Size) 205 assert.EqualValues(t, 1729, d.OpenSize) 206 assert.Equal(t, "repodata/primary.xml.gz", d.Location.Href) 207 case "filelists": 208 assert.EqualValues(t, 257, d.Size) 209 assert.EqualValues(t, 326, d.OpenSize) 210 assert.Equal(t, "repodata/filelists.xml.gz", d.Location.Href) 211 case "other": 212 assert.EqualValues(t, 306, d.Size) 213 assert.EqualValues(t, 394, d.OpenSize) 214 assert.Equal(t, "repodata/other.xml.gz", d.Location.Href) 215 } 216 } 217 }) 218 219 t.Run("repomd.xml.asc", func(t *testing.T) { 220 defer tests.PrintCurrentTest(t)() 221 222 req = NewRequest(t, "GET", url+"/repomd.xml.asc") 223 resp := MakeRequest(t, req, http.StatusOK) 224 225 assert.Contains(t, resp.Body.String(), "-----BEGIN PGP SIGNATURE-----") 226 }) 227 228 decodeGzipXML := func(t testing.TB, resp *httptest.ResponseRecorder, v any) { 229 t.Helper() 230 231 zr, err := gzip.NewReader(resp.Body) 232 assert.NoError(t, err) 233 234 assert.NoError(t, xml.NewDecoder(zr).Decode(v)) 235 } 236 237 t.Run("primary.xml.gz", func(t *testing.T) { 238 defer tests.PrintCurrentTest(t)() 239 240 req = NewRequest(t, "GET", url+"/primary.xml.gz") 241 resp := MakeRequest(t, req, http.StatusOK) 242 243 type EntryList struct { 244 Entries []*rpm_module.Entry `xml:"entry"` 245 } 246 247 type Metadata struct { 248 XMLName xml.Name `xml:"metadata"` 249 Xmlns string `xml:"xmlns,attr"` 250 XmlnsRpm string `xml:"xmlns:rpm,attr"` 251 PackageCount int `xml:"packages,attr"` 252 Packages []struct { 253 XMLName xml.Name `xml:"package"` 254 Type string `xml:"type,attr"` 255 Name string `xml:"name"` 256 Architecture string `xml:"arch"` 257 Version struct { 258 Epoch string `xml:"epoch,attr"` 259 Version string `xml:"ver,attr"` 260 Release string `xml:"rel,attr"` 261 } `xml:"version"` 262 Checksum struct { 263 Checksum string `xml:",chardata"` 264 Type string `xml:"type,attr"` 265 Pkgid string `xml:"pkgid,attr"` 266 } `xml:"checksum"` 267 Summary string `xml:"summary"` 268 Description string `xml:"description"` 269 Packager string `xml:"packager"` 270 URL string `xml:"url"` 271 Time struct { 272 File uint64 `xml:"file,attr"` 273 Build uint64 `xml:"build,attr"` 274 } `xml:"time"` 275 Size struct { 276 Package int64 `xml:"package,attr"` 277 Installed uint64 `xml:"installed,attr"` 278 Archive uint64 `xml:"archive,attr"` 279 } `xml:"size"` 280 Location struct { 281 Href string `xml:"href,attr"` 282 } `xml:"location"` 283 Format struct { 284 License string `xml:"license"` 285 Vendor string `xml:"vendor"` 286 Group string `xml:"group"` 287 Buildhost string `xml:"buildhost"` 288 Sourcerpm string `xml:"sourcerpm"` 289 Provides EntryList `xml:"provides"` 290 Requires EntryList `xml:"requires"` 291 Conflicts EntryList `xml:"conflicts"` 292 Obsoletes EntryList `xml:"obsoletes"` 293 Files []*rpm_module.File `xml:"file"` 294 } `xml:"format"` 295 } `xml:"package"` 296 } 297 298 var result Metadata 299 decodeGzipXML(t, resp, &result) 300 301 assert.EqualValues(t, 1, result.PackageCount) 302 assert.Len(t, result.Packages, 1) 303 p := result.Packages[0] 304 assert.Equal(t, "rpm", p.Type) 305 assert.Equal(t, packageName, p.Name) 306 assert.Equal(t, packageArchitecture, p.Architecture) 307 assert.Equal(t, "YES", p.Checksum.Pkgid) 308 assert.Equal(t, "sha256", p.Checksum.Type) 309 assert.Equal(t, "f1d5d2ffcbe4a7568e98b864f40d923ecca084e9b9bcd5977ed6521c46d3fa4c", p.Checksum.Checksum) 310 assert.Equal(t, "https://gitea.io", p.URL) 311 assert.EqualValues(t, len(content), p.Size.Package) 312 assert.EqualValues(t, 13, p.Size.Installed) 313 assert.EqualValues(t, 272, p.Size.Archive) 314 assert.Equal(t, fmt.Sprintf("package/%s/%s/%s", packageName, packageVersion, packageArchitecture), p.Location.Href) 315 f := p.Format 316 assert.Equal(t, "MIT", f.License) 317 assert.Len(t, f.Provides.Entries, 2) 318 assert.Len(t, f.Requires.Entries, 7) 319 assert.Empty(t, f.Conflicts.Entries) 320 assert.Empty(t, f.Obsoletes.Entries) 321 assert.Len(t, f.Files, 1) 322 }) 323 324 t.Run("filelists.xml.gz", func(t *testing.T) { 325 defer tests.PrintCurrentTest(t)() 326 327 req = NewRequest(t, "GET", url+"/filelists.xml.gz") 328 resp := MakeRequest(t, req, http.StatusOK) 329 330 type Filelists struct { 331 XMLName xml.Name `xml:"filelists"` 332 Xmlns string `xml:"xmlns,attr"` 333 PackageCount int `xml:"packages,attr"` 334 Packages []struct { 335 Pkgid string `xml:"pkgid,attr"` 336 Name string `xml:"name,attr"` 337 Architecture string `xml:"arch,attr"` 338 Version struct { 339 Epoch string `xml:"epoch,attr"` 340 Version string `xml:"ver,attr"` 341 Release string `xml:"rel,attr"` 342 } `xml:"version"` 343 Files []*rpm_module.File `xml:"file"` 344 } `xml:"package"` 345 } 346 347 var result Filelists 348 decodeGzipXML(t, resp, &result) 349 350 assert.EqualValues(t, 1, result.PackageCount) 351 assert.Len(t, result.Packages, 1) 352 p := result.Packages[0] 353 assert.NotEmpty(t, p.Pkgid) 354 assert.Equal(t, packageName, p.Name) 355 assert.Equal(t, packageArchitecture, p.Architecture) 356 assert.Len(t, p.Files, 1) 357 f := p.Files[0] 358 assert.Equal(t, "/usr/local/bin/hello", f.Path) 359 }) 360 361 t.Run("other.xml.gz", func(t *testing.T) { 362 defer tests.PrintCurrentTest(t)() 363 364 req = NewRequest(t, "GET", url+"/other.xml.gz") 365 resp := MakeRequest(t, req, http.StatusOK) 366 367 type Other struct { 368 XMLName xml.Name `xml:"otherdata"` 369 Xmlns string `xml:"xmlns,attr"` 370 PackageCount int `xml:"packages,attr"` 371 Packages []struct { 372 Pkgid string `xml:"pkgid,attr"` 373 Name string `xml:"name,attr"` 374 Architecture string `xml:"arch,attr"` 375 Version struct { 376 Epoch string `xml:"epoch,attr"` 377 Version string `xml:"ver,attr"` 378 Release string `xml:"rel,attr"` 379 } `xml:"version"` 380 Changelogs []*rpm_module.Changelog `xml:"changelog"` 381 } `xml:"package"` 382 } 383 384 var result Other 385 decodeGzipXML(t, resp, &result) 386 387 assert.EqualValues(t, 1, result.PackageCount) 388 assert.Len(t, result.Packages, 1) 389 p := result.Packages[0] 390 assert.NotEmpty(t, p.Pkgid) 391 assert.Equal(t, packageName, p.Name) 392 assert.Equal(t, packageArchitecture, p.Architecture) 393 assert.Len(t, p.Changelogs, 1) 394 c := p.Changelogs[0] 395 assert.Equal(t, "KN4CK3R <dummy@gitea.io>", c.Author) 396 assert.EqualValues(t, 1678276800, c.Date) 397 assert.Equal(t, "- Changelog message.", c.Text) 398 }) 399 }) 400 401 t.Run("Delete", func(t *testing.T) { 402 defer tests.PrintCurrentTest(t)() 403 404 req := NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)) 405 MakeRequest(t, req, http.StatusUnauthorized) 406 407 req = NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)) 408 req = AddBasicAuthHeader(req, user.Name) 409 MakeRequest(t, req, http.StatusNoContent) 410 411 pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeRpm) 412 assert.NoError(t, err) 413 assert.Empty(t, pvs) 414 415 req = NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", rootURL, packageName, packageVersion, packageArchitecture)) 416 req = AddBasicAuthHeader(req, user.Name) 417 MakeRequest(t, req, http.StatusNotFound) 418 }) 419 }