mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-06 22:18:28 +00:00
add codecoverage
This commit is contained in:
committed by
André Roth
parent
3c8defa304
commit
5655480e00
+156
-45
@@ -3,6 +3,9 @@ package api
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
. "gopkg.in/check.v1"
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
@@ -13,6 +16,46 @@ type GPGSuite struct {
|
|||||||
|
|
||||||
var _ = Suite(&GPGSuite{})
|
var _ = Suite(&GPGSuite{})
|
||||||
|
|
||||||
|
func (s *GPGSuite) withFakeGPG(c *C, scriptBody string, test func(scriptPath string)) {
|
||||||
|
tempDir, err := os.MkdirTemp("", "aptly-fake-gpg")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
defer func() { _ = os.RemoveAll(tempDir) }()
|
||||||
|
|
||||||
|
scriptPath := filepath.Join(tempDir, "gpg")
|
||||||
|
err = os.WriteFile(scriptPath, []byte(scriptBody), 0o755)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
oldPath := os.Getenv("PATH")
|
||||||
|
err = os.Setenv("PATH", tempDir+string(os.PathListSeparator)+oldPath)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
defer func() { _ = os.Setenv("PATH", oldPath) }()
|
||||||
|
|
||||||
|
test(scriptPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *GPGSuite) fakeGPGScript(c *C, listOutput string, deleteOutput string, deleteError string) string {
|
||||||
|
return "#!/bin/sh\n" +
|
||||||
|
"if [ \"$1\" = \"--version\" ]; then\n" +
|
||||||
|
" echo 'gpg (GnuPG) 2.2.27'\n" +
|
||||||
|
" exit 0\n" +
|
||||||
|
"fi\n" +
|
||||||
|
"args=\"$*\"\n" +
|
||||||
|
"if printf '%s' \"$args\" | grep -q -- '--list-keys'; then\n" +
|
||||||
|
" cat <<'EOF'\n" + listOutput + "\nEOF\n" +
|
||||||
|
" exit 0\n" +
|
||||||
|
"fi\n" +
|
||||||
|
"if printf '%s' \"$args\" | grep -q -- '--delete-keys'; then\n" +
|
||||||
|
" if [ -n \"" + strings.ReplaceAll(deleteError, "\n", "") + "\" ]; then\n" +
|
||||||
|
" echo '" + strings.ReplaceAll(deleteError, "'", "'\\''") + "'\n" +
|
||||||
|
" exit 1\n" +
|
||||||
|
" fi\n" +
|
||||||
|
" cat <<'EOF'\n" + deleteOutput + "\nEOF\n" +
|
||||||
|
" exit 0\n" +
|
||||||
|
"fi\n" +
|
||||||
|
"echo 'unexpected invocation' >&2\n" +
|
||||||
|
"exit 1\n"
|
||||||
|
}
|
||||||
|
|
||||||
// TestParseGPGOutputEmpty tests parsing of empty GPG output
|
// TestParseGPGOutputEmpty tests parsing of empty GPG output
|
||||||
func (s *GPGSuite) TestParseGPGOutputEmpty(c *C) {
|
func (s *GPGSuite) TestParseGPGOutputEmpty(c *C) {
|
||||||
output := ""
|
output := ""
|
||||||
@@ -173,50 +216,68 @@ fpr:::::::::D8E8F5A516E7A2C4F3E4B5A6C7D8E9F0:`
|
|||||||
|
|
||||||
// TestAPIGPGListKeysDefaultKeyring tests the HTTP endpoint with default keyring
|
// TestAPIGPGListKeysDefaultKeyring tests the HTTP endpoint with default keyring
|
||||||
func (s *GPGSuite) TestAPIGPGListKeysDefaultKeyring(c *C) {
|
func (s *GPGSuite) TestAPIGPGListKeysDefaultKeyring(c *C) {
|
||||||
c.Skip("Requires GPG binary and test key installation. Run manually if needed.")
|
s.withFakeGPG(c, s.fakeGPGScript(c, `pub:u:4096:1:8B48AD6246925553:1611864000:::::
|
||||||
|
uid:u::::1611864000::1234567890::John Doe <john@example.com>::::::::::0:
|
||||||
|
fpr:::::::::D8E8F5A516E7A2C4F3E4B5A6C7D8E9F0:`, "", ""), func(_ string) {
|
||||||
|
response, err := s.HTTPRequest("GET", "/api/gpg/keys", nil)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(response.Code, Equals, 200)
|
||||||
|
|
||||||
response, err := s.HTTPRequest("GET", "/api/gpg/keys", nil)
|
var result gpgKeyListResponse
|
||||||
c.Assert(err, IsNil)
|
err = json.NewDecoder(response.Body).Decode(&result)
|
||||||
c.Check(response.Code, Equals, 200)
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(result.Keys, HasLen, 1)
|
||||||
// Verify response is valid JSON
|
c.Check(result.Keys[0].KeyID, Equals, "8B48AD6246925553")
|
||||||
var result gpgKeyListResponse
|
})
|
||||||
err = json.NewDecoder(response.Body).Decode(&result)
|
|
||||||
c.Assert(err, IsNil)
|
|
||||||
c.Check(result.Keys, NotNil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAPIGPGListKeysWithKeyringParam tests the HTTP endpoint with custom keyring parameter
|
// TestAPIGPGListKeysWithKeyringParam tests the HTTP endpoint with custom keyring parameter
|
||||||
func (s *GPGSuite) TestAPIGPGListKeysWithKeyringParam(c *C) {
|
func (s *GPGSuite) TestAPIGPGListKeysWithKeyringParam(c *C) {
|
||||||
c.Skip("Requires GPG binary and test key installation. Run manually if needed.")
|
argFile, err := os.CreateTemp("", "aptly-gpg-args")
|
||||||
|
|
||||||
response, err := s.HTTPRequest("GET", "/api/gpg/keys?keyring=custom.gpg", nil)
|
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
// May fail if custom.gpg doesn't exist, but endpoint should handle gracefully
|
_ = argFile.Close()
|
||||||
// Accept either 200 (success) or 400 (file not found)
|
defer func() { _ = os.Remove(argFile.Name()) }()
|
||||||
code := response.Code
|
|
||||||
c.Check(code == 200 || code == 400, Equals, true)
|
script := "#!/bin/sh\n" +
|
||||||
|
"if [ \"$1\" = \"--version\" ]; then echo 'gpg (GnuPG) 2.2.27'; exit 0; fi\n" +
|
||||||
|
"printf '%s\n' \"$@\" > '" + argFile.Name() + "'\n" +
|
||||||
|
"if printf '%s' \"$*\" | grep -q -- '--list-keys'; then\n" +
|
||||||
|
"cat <<'EOF'\n" +
|
||||||
|
"pub:u:4096:1:8B48AD6246925553:1611864000:::::\n" +
|
||||||
|
"fpr:::::::::D8E8F5A516E7A2C4F3E4B5A6C7D8E9F0:\n" +
|
||||||
|
"EOF\n" +
|
||||||
|
"exit 0\n" +
|
||||||
|
"fi\n" +
|
||||||
|
"exit 1\n"
|
||||||
|
|
||||||
|
s.withFakeGPG(c, script, func(_ string) {
|
||||||
|
response, reqErr := s.HTTPRequest("GET", "/api/gpg/keys?keyring=/custom.gpg", nil)
|
||||||
|
c.Assert(reqErr, IsNil)
|
||||||
|
c.Check(response.Code, Equals, 200)
|
||||||
|
|
||||||
|
argBytes, readErr := os.ReadFile(argFile.Name())
|
||||||
|
c.Assert(readErr, IsNil)
|
||||||
|
c.Check(string(argBytes), Matches, `(?s).*--keyring\ncustom\.gpg\n.*`)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAPIGPGListKeysResponseFormat tests that the response has the correct structure
|
// TestAPIGPGListKeysResponseFormat tests that the response has the correct structure
|
||||||
func (s *GPGSuite) TestAPIGPGListKeysResponseFormat(c *C) {
|
func (s *GPGSuite) TestAPIGPGListKeysResponseFormat(c *C) {
|
||||||
c.Skip("Requires GPG binary and test key installation. Run manually if needed.")
|
s.withFakeGPG(c, s.fakeGPGScript(c, `pub:f:4096:1:A1B2C3D4E5F67890:1611864000:::::
|
||||||
|
uid:f::::1611864000::1234567890::Jane Smith <jane@example.com>::::::::::0:
|
||||||
|
fpr:::::::::E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4:`, "", ""), func(_ string) {
|
||||||
|
response, err := s.HTTPRequest("GET", "/api/gpg/keys", nil)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(response.Code, Equals, 200)
|
||||||
|
|
||||||
response, err := s.HTTPRequest("GET", "/api/gpg/keys", nil)
|
|
||||||
c.Assert(err, IsNil)
|
|
||||||
|
|
||||||
if response.Code == 200 {
|
|
||||||
var result gpgKeyListResponse
|
var result gpgKeyListResponse
|
||||||
err = json.NewDecoder(response.Body).Decode(&result)
|
err = json.NewDecoder(response.Body).Decode(&result)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
c.Assert(result.Keys, HasLen, 1)
|
||||||
// If there are keys, verify their structure
|
c.Check(result.Keys[0].KeyID, Equals, "A1B2C3D4E5F67890")
|
||||||
for _, key := range result.Keys {
|
c.Check(result.Keys[0].Validity, Equals, "f")
|
||||||
c.Check(key.KeyID, Not(Equals), "")
|
c.Check(result.Keys[0].CreatedAt, Equals, "1611864000")
|
||||||
c.Check(key.Validity, Not(Equals), "")
|
})
|
||||||
c.Check(key.CreatedAt, Not(Equals), "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestParseGPGOutputEdgeCaseUIDWithoutFields tests UID record with missing fields
|
// TestParseGPGOutputEdgeCaseUIDWithoutFields tests UID record with missing fields
|
||||||
@@ -300,8 +361,6 @@ func (s *GPGSuite) TestGPGDeleteKeyParamsValidation(c *C) {
|
|||||||
|
|
||||||
// TestAPIGPGDeleteKeyMissingKeyID tests delete with missing key ID parameter
|
// TestAPIGPGDeleteKeyMissingKeyID tests delete with missing key ID parameter
|
||||||
func (s *GPGSuite) TestAPIGPGDeleteKeyMissingKeyID(c *C) {
|
func (s *GPGSuite) TestAPIGPGDeleteKeyMissingKeyID(c *C) {
|
||||||
c.Skip("Requires GPG binary. Run manually if needed.")
|
|
||||||
|
|
||||||
body, err := json.Marshal(map[string]string{
|
body, err := json.Marshal(map[string]string{
|
||||||
"Keyring": "trustedkeys.gpg",
|
"Keyring": "trustedkeys.gpg",
|
||||||
// GpgKeyID is missing
|
// GpgKeyID is missing
|
||||||
@@ -315,8 +374,6 @@ func (s *GPGSuite) TestAPIGPGDeleteKeyMissingKeyID(c *C) {
|
|||||||
|
|
||||||
// TestAPIGPGDeleteKeyInvalidJSON tests delete with invalid JSON request
|
// TestAPIGPGDeleteKeyInvalidJSON tests delete with invalid JSON request
|
||||||
func (s *GPGSuite) TestAPIGPGDeleteKeyInvalidJSON(c *C) {
|
func (s *GPGSuite) TestAPIGPGDeleteKeyInvalidJSON(c *C) {
|
||||||
c.Skip("Requires GPG binary. Run manually if needed.")
|
|
||||||
|
|
||||||
response, err := s.HTTPRequest("DELETE", "/api/gpg/key", bytes.NewReader([]byte("invalid json")))
|
response, err := s.HTTPRequest("DELETE", "/api/gpg/key", bytes.NewReader([]byte("invalid json")))
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Check(response.Code, Equals, 400)
|
c.Check(response.Code, Equals, 400)
|
||||||
@@ -324,17 +381,71 @@ func (s *GPGSuite) TestAPIGPGDeleteKeyInvalidJSON(c *C) {
|
|||||||
|
|
||||||
// TestAPIGPGDeleteKeySuccess tests successful key deletion
|
// TestAPIGPGDeleteKeySuccess tests successful key deletion
|
||||||
func (s *GPGSuite) TestAPIGPGDeleteKeySuccess(c *C) {
|
func (s *GPGSuite) TestAPIGPGDeleteKeySuccess(c *C) {
|
||||||
c.Skip("Requires GPG binary and test key installation. Run manually if needed.")
|
argFile, err := os.CreateTemp("", "aptly-gpg-delete-args")
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
_ = argFile.Close()
|
||||||
|
defer func() { _ = os.Remove(argFile.Name()) }()
|
||||||
|
|
||||||
body, err := json.Marshal(gpgDeleteKeyParams{
|
script := "#!/bin/sh\n" +
|
||||||
Keyring: "trustedkeys.gpg",
|
"if [ \"$1\" = \"--version\" ]; then echo 'gpg (GnuPG) 2.2.27'; exit 0; fi\n" +
|
||||||
GpgKeyID: "8B48AD6246925553",
|
"printf '%s\n' \"$@\" > '" + argFile.Name() + "'\n" +
|
||||||
|
"if printf '%s' \"$*\" | grep -q -- '--delete-keys'; then\n" +
|
||||||
|
"echo 'deleted'\n" +
|
||||||
|
"exit 0\n" +
|
||||||
|
"fi\n" +
|
||||||
|
"exit 1\n"
|
||||||
|
|
||||||
|
s.withFakeGPG(c, script, func(_ string) {
|
||||||
|
body, marshalErr := json.Marshal(gpgDeleteKeyParams{
|
||||||
|
Keyring: "/trustedkeys.gpg",
|
||||||
|
GpgKeyID: "8B48AD6246925553",
|
||||||
|
})
|
||||||
|
c.Assert(marshalErr, IsNil)
|
||||||
|
|
||||||
|
response, reqErr := s.HTTPRequest("DELETE", "/api/gpg/key", bytes.NewReader(body))
|
||||||
|
c.Assert(reqErr, IsNil)
|
||||||
|
c.Check(response.Code, Equals, 200)
|
||||||
|
c.Check(response.Body.String(), Matches, `"deleted\\n"`)
|
||||||
|
|
||||||
|
argBytes, readErr := os.ReadFile(argFile.Name())
|
||||||
|
c.Assert(readErr, IsNil)
|
||||||
|
argText := string(argBytes)
|
||||||
|
c.Check(argText, Matches, `(?s).*--batch\n--yes\n.*`)
|
||||||
|
c.Check(argText, Matches, `(?s).*--keyring\n/trustedkeys\.gpg\n.*`)
|
||||||
|
c.Check(argText, Matches, `(?s).*--delete-keys\n8B48AD6246925553\n.*`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAPIGPGListKeysCommandFailure tests list error propagation from gpg
|
||||||
|
func (s *GPGSuite) TestAPIGPGListKeysCommandFailure(c *C) {
|
||||||
|
script := "#!/bin/sh\n" +
|
||||||
|
"if [ \"$1\" = \"--version\" ]; then echo 'gpg (GnuPG) 2.2.27'; exit 0; fi\n" +
|
||||||
|
"if printf '%s' \"$*\" | grep -q -- '--list-keys'; then\n" +
|
||||||
|
"echo 'keyring missing'\n" +
|
||||||
|
"exit 1\n" +
|
||||||
|
"fi\n" +
|
||||||
|
"exit 1\n"
|
||||||
|
|
||||||
|
s.withFakeGPG(c, script, func(_ string) {
|
||||||
|
response, err := s.HTTPRequest("GET", "/api/gpg/keys", nil)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(response.Code, Equals, 400)
|
||||||
|
c.Check(response.Body.String(), Matches, `(?s).*failed to list keys: keyring missing.*`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAPIGPGDeleteKeyCommandFailure tests delete error propagation from gpg
|
||||||
|
func (s *GPGSuite) TestAPIGPGDeleteKeyCommandFailure(c *C) {
|
||||||
|
s.withFakeGPG(c, s.fakeGPGScript(c, "", "", "delete failed"), func(_ string) {
|
||||||
|
body, err := json.Marshal(gpgDeleteKeyParams{
|
||||||
|
Keyring: "trustedkeys.gpg",
|
||||||
|
GpgKeyID: "8B48AD6246925553",
|
||||||
|
})
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
response, reqErr := s.HTTPRequest("DELETE", "/api/gpg/key", bytes.NewReader(body))
|
||||||
|
c.Assert(reqErr, IsNil)
|
||||||
|
c.Check(response.Code, Equals, 400)
|
||||||
|
c.Check(response.Body.String(), Matches, `(?s).*failed to delete key: delete failed.*`)
|
||||||
})
|
})
|
||||||
c.Assert(err, IsNil)
|
|
||||||
|
|
||||||
response, err := s.HTTPRequest("DELETE", "/api/gpg/key", bytes.NewReader(body))
|
|
||||||
c.Assert(err, IsNil)
|
|
||||||
// Should succeed (200) or fail gracefully (400) if key doesn't exist
|
|
||||||
code := response.Code
|
|
||||||
c.Check(code == 200 || code == 400, Equals, true)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user