Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,10 @@ Or run a specific suite:
```bash
CONFIG_PATH="$(pwd)/configs/" go test ./internal/test/integration/ -run TestPythonSuite -v
CONFIG_PATH="$(pwd)/configs/" go test ./internal/test/integration/ -run TestRpmSuite -v
CONFIG_PATH="$(pwd)/configs/" go test ./internal/test/integration/ -run TestMavenSuite -v
```

The Python integration test syncs `shelf-reader` from PyPI into a random domain via the Pulp API, then asserts tangy can read it from the database. Test data is left in the database after a run; use `make compose-clean` to wipe volumes and start fresh.
The Python integration test syncs `shelf-reader` from PyPI into a random domain via the Pulp API, then asserts tangy can read it from the database. The Maven integration test pull-through caches `junit:junit:4.13.2` from a local nginx fixture in the compose stack (avoids Maven Central rate limits in CI), adds the cached content to a repository, then asserts tangy can read it from the database. Test data is left in the database after a run; use `make compose-clean` to wipe volumes and start fresh.

### Mocking
Tangy also exports a mock interface you can regenerate using the [mockery](https://github.com/vektra/mockery) tool.

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions compose_files/pulp/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ services:
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]

maven_fixture:
image: "docker.io/library/nginx:alpine"
volumes:
- "./assets/maven-fixture:/usr/share/nginx/html:ro"
restart: always

pulp_api:
image: "quay.io/redhat-services-prod/pulp-services-tenant/pulp:latest"
platform: linux/amd64
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/content-services/tang
go 1.24.0

require (
github.com/content-services/zest/release/v2024 v2024.12.1734541842
github.com/content-services/zest/release/v2026 v2026.7.1783082453
github.com/google/uuid v1.6.0
github.com/jackc/pgx-zerolog v0.0.0-20230315001418-f978528409eb
github.com/jackc/pgx/v5 v5.7.6
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/content-services/zest/release/v2024 v2024.12.1734541842 h1:vIIWFZ5j76MVg2VuxkXAR56NAvkst7LtAeAugbfJmFU=
github.com/content-services/zest/release/v2024 v2024.12.1734541842/go.mod h1:fUWkjhvNTiS9UaaAWNLYkshl37V9IVmmRaprp9eDmrU=
github.com/content-services/zest/release/v2026 v2026.7.1783082453 h1:mZ8LExJlcMcoIUCv7eW+qQwZ9in18byKtuA99WWG0+M=
github.com/content-services/zest/release/v2026 v2026.7.1783082453/go.mod h1:pThh4a5Qm53BZ5V3WqRkWx7WBrIaeDdAnTmmqi6j8Vw=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
Expand Down
266 changes: 266 additions & 0 deletions internal/test/integration/maven_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
package integration

import (
"context"
"strings"
"testing"

"github.com/content-services/tang/internal/config"
"github.com/content-services/tang/internal/zestwrapper"
"github.com/content-services/tang/pkg/tangy"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

const (
testMavenRepoName = "junit-fixture"
testMavenDistributionBasePath = "junit-fixture"
testMavenRemoteURL = "http://maven_fixture/"
testMavenContentOrigin = "http://localhost:8088"
testMavenGroupID = "junit"
testMavenArtifactID = "junit"
testMavenVersion = "4.13.2"
testMavenPomPath = "junit/junit/4.13.2/junit-4.13.2.pom"
)

type MavenSuite struct {
suite.Suite
client *zestwrapper.MavenZest
tangy tangy.Tangy
domainName string
repositoryHref string
}

func (m *MavenSuite) createTestRepository(t *testing.T) {
_, err := m.client.LookupOrCreateDomain(m.domainName)
require.NoError(t, err)

repoHref, remoteHref, err := m.client.CreateRepository(
m.domainName,
testMavenRepoName,
testMavenRemoteURL,
testMavenDistributionBasePath,
)
require.NoError(t, err)

err = m.client.FetchArtifact(testMavenContentOrigin, m.domainName, testMavenDistributionBasePath, testMavenPomPath)
require.NoError(t, err)

addTask, err := m.client.AddCachedContent(repoHref, remoteHref)
require.NoError(t, err)

_, err = m.client.PollTask(addTask)
require.NoError(t, err)

m.repositoryHref = repoHref
}

func TestMavenSuite(t *testing.T) {
s := config.Get().Server
mavenZest := zestwrapper.NewMavenZest(context.Background(), s)

dbConfig := config.Get().Database
ta, err := tangy.New(tangy.Database{
Name: dbConfig.Name,
Host: dbConfig.Host,
Port: dbConfig.Port,
User: dbConfig.User,
Password: dbConfig.Password,
}, tangy.Logger{Enabled: true, Logger: &log.Logger, LogLevel: zerolog.LevelDebugValue})
require.NoError(t, err)
t.Cleanup(ta.Close)

m := MavenSuite{}
m.client = &mavenZest
m.tangy = ta
m.domainName = RandStringBytes(10)

m.createTestRepository(t)

suite.Run(t, &m)
}

func (m *MavenSuite) TestMavenPackageList() {
response, err := m.tangy.MavenPackageList(context.Background(), m.repositoryHref, tangy.MavenPackageListFilters{}, tangy.PageOptions{
Offset: 0,
Limit: 10,
})
require.NoError(m.T(), err)
require.NotEmpty(m.T(), response.Results)
assert.Equal(m.T(), 1, response.Total)

pkg := response.Results[0]
assert.Equal(m.T(), testMavenGroupID, pkg.GroupID)
assert.Equal(m.T(), testMavenArtifactID, pkg.ArtifactID)
assert.Contains(m.T(), pkg.Versions, testMavenVersion)
require.NotEmpty(m.T(), pkg.LatestReleases)

foundVersion := false
for _, latest := range pkg.LatestReleases {
if latest.Version == testMavenVersion {
foundVersion = true
assert.NotEmpty(m.T(), latest.CreatedAt)
}
}
assert.True(m.T(), foundVersion)
}

func (m *MavenSuite) TestMavenPackageListSearchFilter() {
response, err := m.tangy.MavenPackageList(context.Background(), m.repositoryHref, tangy.MavenPackageListFilters{Search: "junit"}, tangy.PageOptions{
Offset: 0,
Limit: 10,
})
require.NoError(m.T(), err)
require.NotEmpty(m.T(), response.Results)
assert.Equal(m.T(), 1, response.Total)
assert.Equal(m.T(), testMavenArtifactID, response.Results[0].ArtifactID)

response, err = m.tangy.MavenPackageList(context.Background(), m.repositoryHref, tangy.MavenPackageListFilters{Search: "nonexistent-artifact"}, tangy.PageOptions{
Offset: 0,
Limit: 10,
})
require.NoError(m.T(), err)
assert.Empty(m.T(), response.Results)
assert.Zero(m.T(), response.Total)
}

func (m *MavenSuite) TestMavenPackageListPagination() {
response, err := m.tangy.MavenPackageList(context.Background(), m.repositoryHref, tangy.MavenPackageListFilters{}, tangy.PageOptions{
Offset: 0,
Limit: 1,
})
require.NoError(m.T(), err)
assert.Len(m.T(), response.Results, 1)
assert.Equal(m.T(), 1, response.Total)
assert.Equal(m.T(), 1, response.Limit)

response, err = m.tangy.MavenPackageList(context.Background(), m.repositoryHref, tangy.MavenPackageListFilters{}, tangy.PageOptions{
Offset: 1,
Limit: 1,
})
require.NoError(m.T(), err)
assert.Empty(m.T(), response.Results)
assert.Equal(m.T(), 1, response.Total)
}

func (m *MavenSuite) TestMavenPackageListEmptyHref() {
response, err := m.tangy.MavenPackageList(context.Background(), "", tangy.MavenPackageListFilters{}, tangy.PageOptions{Limit: 10})
require.NoError(m.T(), err)
assert.Empty(m.T(), response.Results)
assert.Zero(m.T(), response.Total)
}

func (m *MavenSuite) TestMavenBuildList() {
response, err := m.tangy.MavenBuildList(
context.Background(),
m.repositoryHref,
testMavenGroupID,
testMavenArtifactID,
testMavenVersion,
tangy.PageOptions{Offset: 0, Limit: 10},
)
require.NoError(m.T(), err)
require.NotEmpty(m.T(), response.Results)
assert.Equal(m.T(), 1, response.Total)
assert.Equal(m.T(), 10, response.Limit)

build := response.Results[0]
assert.Equal(m.T(), testMavenGroupID, build.GroupID)
assert.Equal(m.T(), testMavenArtifactID, build.ArtifactID)
assert.Equal(m.T(), testMavenVersion, build.Version)
assert.True(m.T(), strings.HasSuffix(build.Filename, ".pom"), build.Filename)
assert.NotEmpty(m.T(), build.CreatedAt)
}

func (m *MavenSuite) TestMavenBuildListPagination() {
response, err := m.tangy.MavenBuildList(
context.Background(),
m.repositoryHref,
testMavenGroupID,
testMavenArtifactID,
testMavenVersion,
tangy.PageOptions{Offset: 0, Limit: 1},
)
require.NoError(m.T(), err)
assert.Len(m.T(), response.Results, 1)
assert.Equal(m.T(), 1, response.Total)

response, err = m.tangy.MavenBuildList(
context.Background(),
m.repositoryHref,
testMavenGroupID,
testMavenArtifactID,
testMavenVersion,
tangy.PageOptions{Offset: 1, Limit: 10},
)
require.NoError(m.T(), err)
assert.Empty(m.T(), response.Results)
assert.Equal(m.T(), 1, response.Total)
}

func (m *MavenSuite) TestMavenBuildListEmptyHref() {
response, err := m.tangy.MavenBuildList(context.Background(), "", testMavenGroupID, testMavenArtifactID, testMavenVersion, tangy.PageOptions{Limit: 10})
require.NoError(m.T(), err)
assert.Empty(m.T(), response.Results)
assert.Zero(m.T(), response.Total)
}

func (m *MavenSuite) TestMavenBuildListOptionalFilters() {
response, err := m.tangy.MavenBuildList(
context.Background(),
m.repositoryHref,
"",
"",
"",
tangy.PageOptions{Offset: 0, Limit: 10},
)
require.NoError(m.T(), err)
require.NotEmpty(m.T(), response.Results)
assert.Equal(m.T(), 1, response.Total)

build := response.Results[0]
assert.Equal(m.T(), testMavenGroupID, build.GroupID)
assert.Equal(m.T(), testMavenArtifactID, build.ArtifactID)
assert.Equal(m.T(), testMavenVersion, build.Version)

filtered, err := m.tangy.MavenBuildList(
context.Background(),
m.repositoryHref,
testMavenGroupID,
"",
"",
tangy.PageOptions{Offset: 0, Limit: 10},
)
require.NoError(m.T(), err)
require.Len(m.T(), filtered.Results, 1)
assert.Equal(m.T(), 1, filtered.Total)

filtered, err = m.tangy.MavenBuildList(
context.Background(),
m.repositoryHref,
"",
testMavenArtifactID,
"",
tangy.PageOptions{Offset: 0, Limit: 10},
)
require.NoError(m.T(), err)
require.Len(m.T(), filtered.Results, 1)
assert.Equal(m.T(), 1, filtered.Total)
}

func (m *MavenSuite) TestMavenRepositoryMetrics() {
metrics, err := m.tangy.MavenRepositoryMetrics(context.Background(), m.repositoryHref)
require.NoError(m.T(), err)
assert.Equal(m.T(), 1, metrics.PackageCount)
assert.Equal(m.T(), 1, metrics.BuildCount)
}

func (m *MavenSuite) TestMavenRepositoryMetricsEmptyHref() {
metrics, err := m.tangy.MavenRepositoryMetrics(context.Background(), "")
require.NoError(m.T(), err)
assert.Zero(m.T(), metrics.PackageCount)
assert.Zero(m.T(), metrics.BuildCount)
}
11 changes: 11 additions & 0 deletions internal/zestwrapper/href.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package zestwrapper

import "strings"

// normalizePulpHref strips a leading slash from Pulp resource hrefs before they are
// embedded in zest URL paths. Zest v2026 builds paths as baseURL + "/" + href, so hrefs
// returned by Pulp (e.g. "/api/pulp/default/api/v3/...") would otherwise produce a
// double slash and 404 responses.
func normalizePulpHref(href string) string {
return strings.TrimPrefix(href, "/")
}
33 changes: 33 additions & 0 deletions internal/zestwrapper/href_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package zestwrapper

import "testing"

func TestNormalizePulpHref(t *testing.T) {
t.Parallel()

tests := []struct {
name string
href string
want string
}{
{
name: "leading slash",
href: "/api/pulp/default/api/v3/tasks/abc/",
want: "api/pulp/default/api/v3/tasks/abc/",
},
{
name: "no leading slash",
href: "api/pulp/default/api/v3/tasks/abc/",
want: "api/pulp/default/api/v3/tasks/abc/",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := normalizePulpHref(tt.href); got != tt.want {
t.Fatalf("normalizePulpHref() = %q, want %q", got, tt.want)
}
})
}
}
Loading
Loading