mirror of
https://github.com/cooperspencer/gickup.git
synced 2026-10-19 16:16:46 +00:00
Pass the resolved destination credential through GetOrCreate to the push helper. Cover App and configured tokens on github.com and Enterprise Server, API failures, and the backup call chain.
98 lines
2.8 KiB
Go
98 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cooperspencer/gickup/types"
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/go-git/go-git/v5/plumbing/object"
|
|
)
|
|
|
|
//nolint:paralleltest // backup calls providers that assign package-level loggers.
|
|
func TestBackupGitHubAppPushCredential(t *testing.T) {
|
|
const wantToken = "installation-token-for-test"
|
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
keyFile := filepath.Join(t.TempDir(), "app.pem")
|
|
if err := os.WriteFile(keyFile, pem.EncodeToMemory(&pem.Block{
|
|
Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key),
|
|
}), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sourceDir := t.TempDir()
|
|
source, err := git.PlainInit(sourceDir, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
worktree, err := source.Worktree()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := worktree.Commit("seed", &git.CommitOptions{
|
|
AllowEmptyCommits: true,
|
|
Author: &object.Signature{Name: "test", Email: "test@example.com", When: time.Now()},
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var passwordsMu sync.Mutex
|
|
var passwords []string
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
switch r.Method + " " + r.URL.Path {
|
|
case "POST /api/v3/app/installations/42/access_tokens":
|
|
w.WriteHeader(http.StatusCreated)
|
|
_ = json.NewEncoder(w).Encode(map[string]string{
|
|
"token": wantToken, "expires_at": time.Now().Add(time.Hour).UTC().Format(time.RFC3339),
|
|
})
|
|
case "GET /api/v3/orgs/example":
|
|
_, _ = w.Write([]byte(`{"login":"example"}`))
|
|
case "GET /api/v3/repos/example/repo":
|
|
_ = json.NewEncoder(w).Encode(map[string]string{"clone_url": "http://" + r.Host + "/example/repo.git"})
|
|
case "GET /example/repo.git/info/refs":
|
|
_, password, _ := r.BasicAuth()
|
|
passwordsMu.Lock()
|
|
passwords = append(passwords, password)
|
|
passwordsMu.Unlock()
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
default:
|
|
t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
backup([]types.Repo{{Name: "repo", URL: sourceDir}}, &types.Conf{
|
|
Destination: types.Destination{Github: []types.GenRepo{{
|
|
URL: server.URL, Organization: "example",
|
|
AppID: 1, AppInstallationID: 42, AppPrivateKeyFile: keyFile,
|
|
}}},
|
|
})
|
|
passwordsMu.Lock()
|
|
gotPasswords := passwords
|
|
passwords = nil
|
|
passwordsMu.Unlock()
|
|
if len(gotPasswords) == 0 {
|
|
t.Fatal("backup did not attempt a push")
|
|
}
|
|
for _, got := range gotPasswords {
|
|
if got != wantToken {
|
|
t.Fatalf("backup push password = %q, want resolved installation token", got)
|
|
}
|
|
}
|
|
}
|