mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
303 lines
16 KiB
Go
303 lines
16 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hanzoai/git/models/packages"
|
|
"github.com/hanzoai/git/models/unittest"
|
|
user_model "github.com/hanzoai/git/models/user"
|
|
"github.com/hanzoai/git/tests"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestPackageTerraform(t *testing.T) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
|
|
packageName := "te-st_pac.kage"
|
|
// generate the state json
|
|
genState := func(serial int) string {
|
|
return fmt.Sprintf(`{
|
|
"version": 4,
|
|
"terraform_version": "1.10.4",
|
|
"serial": %d,
|
|
"lineage": "bca3c5f6-01dc-cdad-5310-d1b12e02e430",
|
|
"outputs": {},
|
|
"resources": [{
|
|
"mode": "managed",
|
|
"type": "hello",
|
|
"name": "null_resource",
|
|
"provider": "provider[\"registry.terraform.io/hashicorp/null\"]",
|
|
"instances": [{
|
|
"schema_version": 0,
|
|
"attributes": {
|
|
"id": "3832416504545530133",
|
|
"triggers": null
|
|
},
|
|
"sensitive_attributes": []
|
|
}]
|
|
}],
|
|
"check_results": null
|
|
}`, serial)
|
|
}
|
|
genEncryptedState := func(serial int) string {
|
|
// json taken from wireshark inspection
|
|
return fmt.Sprintf(`{
|
|
"serial": %d,
|
|
"lineage": "4dad5e35-cbd5-ca2f-3c20-97c5e13b7033",
|
|
"meta": {
|
|
"key_provider.pbkdf2.foo": "eyJzYWx0IjoiWE55NnRDZTlSQnFNWGRqWm5xc202TU1DREZNbW5FbXRHczc2UXI0NEpXST0iLCJpdGVyYXRpb25zIjo2MDAwMDAsImhhc2hfZnVuY3Rpb24iOiJzaGE1MTIiLCJrZXlfbGVuZ3RoIjozMn0="
|
|
},
|
|
"encrypted_data": "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",
|
|
"encryption_version": "v0"
|
|
}`, serial)
|
|
}
|
|
genLock := func(uuid string) string {
|
|
return fmt.Sprintf(`{
|
|
"ID": "%s",
|
|
"Operation": "OperationTypePlan",
|
|
"Info": "",
|
|
"Who": "test-user@localhost",
|
|
"Version": "1.0",
|
|
"Created": "2023-01-01T00:00:00Z",
|
|
"Path": "test.tfstate"
|
|
}`, uuid)
|
|
}
|
|
|
|
url := fmt.Sprintf("/v1/packages/%s/terraform/state/%s", user.Name, packageName)
|
|
lockURL := fmt.Sprintf("/v1/packages/%s/terraform/state/%s/lock", user.Name, packageName)
|
|
|
|
// Covers non-existing package retrieval and deletion
|
|
t.Run("GetOrDeleteNonExisting", func(t *testing.T) {
|
|
// Package does not exist yet
|
|
req := NewRequest(t, "GET", url).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
// So deleting it also should not work
|
|
req = NewRequest(t, "DELETE", url).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusNotFound)
|
|
})
|
|
t.Run("RegularOperations", func(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
statefunc func(int) string
|
|
}{
|
|
{
|
|
name: "Plain",
|
|
statefunc: genState,
|
|
},
|
|
{
|
|
name: "Encrypted",
|
|
statefunc: genEncryptedState,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// 1. Lock the state
|
|
lockID := uuid.New().String()
|
|
lockInfo := genLock(lockID)
|
|
req := NewRequestWithBody(t, "POST", lockURL, strings.NewReader(lockInfo)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Verify lock property in DB
|
|
p, err := packages.GetPackageByName(t.Context(), user.ID, packages.TypeTerraformState, packageName)
|
|
require.NoError(t, err)
|
|
props, err := packages.GetPropertiesByName(t.Context(), packages.PropertyTypePackage, p.ID, "terraform.lock")
|
|
require.NoError(t, err)
|
|
require.Len(t, props, 1)
|
|
assert.Contains(t, props[0].Value, lockID)
|
|
|
|
// Upload state with correct Lock ID
|
|
state1 := tc.statefunc(1)
|
|
req = NewRequestWithBody(t, "POST", url+"?ID="+lockID, strings.NewReader(state1)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusCreated)
|
|
|
|
// Verify version created
|
|
pv, err := packages.GetVersionByNameAndVersion(t.Context(), user.ID, packages.TypeTerraformState, packageName, "1")
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, pv)
|
|
|
|
// 3. Unlock the state
|
|
req = NewRequestWithBody(t, "DELETE", lockURL, strings.NewReader(lockInfo)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Verify lock property is cleared
|
|
props, err = packages.GetPropertiesByName(t.Context(), packages.PropertyTypePackage, p.ID, "terraform.lock")
|
|
require.NoError(t, err)
|
|
require.Len(t, props, 1)
|
|
assert.Empty(t, props[0].Value)
|
|
|
|
// Get latest state
|
|
req = NewRequest(t, "GET", url).AddBasicAuth(user.Name)
|
|
resp := MakeRequest(t, req, http.StatusOK)
|
|
assert.Equal(t, state1, resp.Body.String())
|
|
|
|
// Upload new version without lock
|
|
state2 := genState(2)
|
|
req = NewRequestWithBody(t, "POST", url, strings.NewReader(state2)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusCreated)
|
|
|
|
// 6. Delete the entire package
|
|
req = NewRequest(t, "DELETE", url).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Verify package is deleted from DB
|
|
_, err = packages.GetPackageByName(t.Context(), user.ID, packages.TypeTerraformState, packageName)
|
|
assert.ErrorIs(t, err, packages.ErrPackageNotExist)
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("StateHistory", func(t *testing.T) {
|
|
// Upload 3 versions
|
|
for i := range 3 {
|
|
state := genState(i + 1) // 1-based
|
|
req := NewRequestWithBody(t, "POST", url, strings.NewReader(state)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusCreated)
|
|
}
|
|
|
|
// Verify latest is 3
|
|
req := NewRequest(t, "GET", url).AddBasicAuth(user.Name)
|
|
resp := MakeRequest(t, req, http.StatusOK)
|
|
assert.Equal(t, genState(3), resp.Body.String())
|
|
|
|
// Verify version 2 is accessible
|
|
req = NewRequest(t, "GET", url+"/versions/2").AddBasicAuth(user.Name)
|
|
resp = MakeRequest(t, req, http.StatusOK)
|
|
assert.Equal(t, genState(2), resp.Body.String())
|
|
|
|
// Delete version 2
|
|
req = NewRequest(t, "DELETE", url+"/versions/2").AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusNoContent)
|
|
|
|
// Verify version 2 is gone from DB
|
|
_, err := packages.GetVersionByNameAndVersion(t.Context(), user.ID, packages.TypeTerraformState, packageName, "2")
|
|
assert.ErrorIs(t, err, packages.ErrPackageNotExist)
|
|
|
|
// Verify version 2 is gone from API
|
|
req = NewRequest(t, "GET", url+"/versions/2").AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusNotFound)
|
|
|
|
// Deleting latest version (3) should be forbidden
|
|
req = NewRequest(t, "DELETE", url+"/versions/3").AddBasicAuth(user.Name)
|
|
resp = MakeRequest(t, req, http.StatusForbidden)
|
|
assert.Contains(t, resp.Body.String(), "cannot delete the latest version")
|
|
|
|
// Cleanup
|
|
req = NewRequest(t, "DELETE", url).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
})
|
|
|
|
t.Run("BadOperations", func(t *testing.T) {
|
|
t.Run("LockingIssues", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
lockID1 := uuid.New().String()
|
|
lockID2 := uuid.New().String()
|
|
lockInfo1 := genLock(lockID1)
|
|
lockInfo2 := genLock(lockID2)
|
|
|
|
// Pre-create package - it's required for unlock on the non-locked package to work
|
|
req := NewRequestWithBody(t, "POST", url, strings.NewReader(genState(1))).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusCreated)
|
|
|
|
// Unlock non-locked state (should return 200)
|
|
req = NewRequestWithBody(t, "DELETE", lockURL, strings.NewReader(lockInfo1)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Lock the state
|
|
req = NewRequestWithBody(t, "POST", lockURL, strings.NewReader(lockInfo1)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Another lock attempt should fail
|
|
req = NewRequestWithBody(t, "POST", lockURL, strings.NewReader(lockInfo2)).AddBasicAuth(user.Name)
|
|
resp := MakeRequest(t, req, http.StatusLocked)
|
|
assert.JSONEq(t, lockInfo1, resp.Body.String())
|
|
|
|
// Unlock with wrong ID should fail
|
|
req = NewRequestWithBody(t, "DELETE", lockURL, strings.NewReader(lockInfo2)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusLocked)
|
|
|
|
// Same user locking again should fail (already locked)
|
|
req = NewRequestWithBody(t, "POST", lockURL, strings.NewReader(lockInfo1)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusLocked)
|
|
|
|
// Unlock with correct ID
|
|
req = NewRequestWithBody(t, "DELETE", lockURL, strings.NewReader(lockInfo1)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Clean up
|
|
req = NewRequest(t, "DELETE", url).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
})
|
|
|
|
t.Run("UploadWithoutValidLock", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
lockID := uuid.New().String()
|
|
lockInfo := genLock(lockID)
|
|
|
|
// Lock the state
|
|
req := NewRequestWithBody(t, "POST", lockURL, strings.NewReader(lockInfo)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Upload without ID should fail
|
|
req = NewRequestWithBody(t, "POST", url, strings.NewReader(genState(1))).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusLocked)
|
|
|
|
// Upload with wrong ID should fail
|
|
req = NewRequestWithBody(t, "POST", url+"?ID="+uuid.New().String(), strings.NewReader(genState(1))).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusLocked)
|
|
|
|
// Cleanup lock
|
|
req = NewRequestWithBody(t, "DELETE", lockURL, strings.NewReader(lockInfo)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
})
|
|
|
|
t.Run("DeleteWithLock", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
// Create package and lock it
|
|
req := NewRequestWithBody(t, "POST", url, strings.NewReader(genState(1))).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusCreated)
|
|
|
|
lockID := uuid.New().String()
|
|
lockInfo := genLock(lockID)
|
|
req = NewRequestWithBody(t, "POST", lockURL, strings.NewReader(lockInfo)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
|
|
// Delete package should fail
|
|
req = NewRequest(t, "DELETE", url).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusLocked)
|
|
|
|
// Verify package exists
|
|
p, err := packages.GetPackageByName(t.Context(), user.ID, packages.TypeTerraformState, packageName)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, p)
|
|
|
|
// Cleanup
|
|
req = NewRequestWithBody(t, "DELETE", lockURL, strings.NewReader(lockInfo)).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
req = NewRequest(t, "DELETE", url).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusOK)
|
|
})
|
|
t.Run("PutEmpty", func(t *testing.T) {
|
|
defer tests.PrintCurrentTest(t)()
|
|
|
|
// safeguard against null payload
|
|
req := NewRequestWithBody(t, "POST", url, strings.NewReader("null")).AddBasicAuth(user.Name)
|
|
MakeRequest(t, req, http.StatusBadRequest)
|
|
})
|
|
})
|
|
}
|