Skip to content
2 changes: 1 addition & 1 deletion mise.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html
# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html

[[tools."aqua:minio/mc"]]
version = "RELEASE.2025-04-08T15-39-49Z"
Expand Down
40 changes: 32 additions & 8 deletions tests/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,28 @@ func TestSameVersion(t *testing.T) {
ctx, client, stop := setup()
defer stop()

modID := RunVersionTest(ctx, t, client, "testdata/DuplicateMod.smod", false, "DuplicateMod", "", "")
RunVersionTest(ctx, t, client, "testdata/DuplicateMod.smod", false, "DuplicateMod", modID, "this mod already has a version with this name")
modID, _, _ := RunVersionTest(ctx, t, client, "testdata/DuplicateMod.smod", false, "DuplicateMod", "", "", "")
RunVersionTest(ctx, t, client, "testdata/DuplicateMod.smod", false, "DuplicateMod", modID, "this mod already has a published version with this name", "")
}

func TestDeletedVersionSemverReuse(t *testing.T) {
ctx, client, stop := setup()
defer stop()

modID, versionID, token := RunVersionTest(ctx, t, client, "testdata/DuplicateMod.smod", false, "DuplicateMod", "", "", "")

deleteRequest := authRequest(`mutation DeleteVersion($versionId: VersionID!) {
deleteVersion(versionId: $versionId)
}`, token)
deleteRequest.Var("versionId", versionID)

var deleteResponse struct {
DeleteVersion bool
}
testza.AssertNoError(t, client.Run(ctx, deleteRequest, &deleteResponse))
testza.AssertTrue(t, deleteResponse.DeleteVersion)

RunVersionTest(ctx, t, client, "testdata/DuplicateMod.smod", false, "DuplicateMod", modID, "reusing the version name of a deleted mod version is not allowed", token)
}

func TestModWithMissingDependency(t *testing.T) {
Expand All @@ -72,20 +92,24 @@ func TestModMalformedJSON(t *testing.T) {
func RunVersionTestWrapper(t *testing.T, modFilePath string, executeVirusCheck bool, modReference string, expectError string) {
ctx, client, stop := setup()
defer stop()
RunVersionTest(ctx, t, client, modFilePath, executeVirusCheck, modReference, "", expectError)
RunVersionTest(ctx, t, client, modFilePath, executeVirusCheck, modReference, "", expectError, "")
}

func RunVersionTest(ctx context.Context, t *testing.T, client *graphql.Client, modFilePath string, executeVirusCheck bool, modReference string, reuseModID string, expectError string) string {
func RunVersionTest(ctx context.Context, t *testing.T, client *graphql.Client, modFilePath string, executeVirusCheck bool, modReference string, reuseModID string, expectError string, providedToken string) (string, string, string) {
if executeVirusCheck && (!viper.IsSet("virustotal.key") || viper.GetString("virustotal.key") == "") {
println("missing virustotal key from config, skipping")
t.SkipNow()
return ""
return "", "", ""
}

viper.Set("skip-virus-check", !executeVirusCheck)

token, _, err := makeUser(ctx)
testza.AssertNoError(t, err)
token := providedToken
if providedToken == "" {
var err error
token, _, err = makeUser(ctx)
testza.AssertNoError(t, err, token)
}

modID := reuseModID
if modID == "" {
Expand Down Expand Up @@ -394,5 +418,5 @@ func RunVersionTest(ctx context.Context, t *testing.T, client *graphql.Client, m
})
}

return modID
return modID, versionID, token
}
18 changes: 17 additions & 1 deletion workflows/versionupload/extract_mod_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"go.temporal.io/sdk/temporal"

"github.com/satisfactorymodding/smr-api/db"
"github.com/satisfactorymodding/smr-api/db/schema"
version2 "github.com/satisfactorymodding/smr-api/generated/ent/version"
"github.com/satisfactorymodding/smr-api/util"
"github.com/satisfactorymodding/smr-api/validation"
Expand Down Expand Up @@ -59,7 +60,22 @@ func (*A) ExtractModInfoActivity(ctx context.Context, args ExtractModInfoArgs) (
}

if count > 0 {
return nil, temporal.NewNonRetryableApplicationError("this mod already has a version with this name", "fatal", nil)
return nil, temporal.NewNonRetryableApplicationError("this mod already has a published version with this name", "fatal", nil)
}

countIncludingDeleted, err := db.From(ctx).Version.Query().
Where(
version2.ModID(mod.ID),
version2.Version(modInfo.Version),
version2.DeletedAtNotNil(),
).
Count(schema.SkipSoftDelete(ctx))
if err != nil {
return nil, temporal.NewNonRetryableApplicationError("database error", "fatal", err)
}

if countIncludingDeleted > 0 {
return nil, temporal.NewNonRetryableApplicationError("reusing the version name of a deleted mod version is not allowed", "fatal", nil)
}

// Allow only new 5 versions per 24h
Expand Down
Loading