package httpapi import ( "context" "crypto/ed25519" "encoding/base64" "os" "os/exec" "path/filepath" "strings" "testing" "time" "net/http/httptest" "evobgp/internal/bundle" ) // TestLegacyNodeBinaryCurrentControlPlane exercises an independently built old // CLI against today's HTTP handlers. It never calls apply or touches host BIRD. func TestLegacyNodeBinaryCurrentControlPlane(t *testing.T) { binary := os.Getenv("EVOBGP_TEST_LEGACY_NODE_BIN") if binary == "" { t.Skip("EVOBGP_TEST_LEGACY_NODE_BIN not set") } binary, err := filepath.Abs(binary) if err != nil { t.Fatal(err) } if _, err := os.Stat(binary); err != nil { t.Fatal(err) } srv, err := New(Options{SeedDemo: true, BundleSeedHex: testBundleSeed}) if err != nil { t.Fatal(err) } defer srv.Close() tenant, _, _, revision, speaker := srv.Store().DemoIDs() mustSetTestAPIKeys(t, srv, "legacy-node|"+tenant+"|node,other-node|other-tenant|node") if err := srv.Store().PublishRevisionForSpeaker(speaker, revision); err != nil { t.Fatal(err) } server := httptest.NewServer(srv.Handler()) defer server.Close() run := func(wantSuccess bool, args ...string) { t.Helper() ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() output, err := exec.CommandContext(ctx, binary, args...).CombinedOutput() if ctx.Err() != nil { t.Fatalf("legacy CLI timed out: %v", ctx.Err()) } if wantSuccess && err != nil { t.Fatalf("legacy CLI failed: %v: %s", err, output) } if !wantSuccess && err == nil { t.Fatalf("legacy CLI unexpectedly accepted invalid request: %s", output) } } path := filepath.Join(t.TempDir(), "current-bundle.tar.gz") run(true, "pull-bundle", "-base-url", server.URL, "-token", "legacy-node", "-speaker-id", speaker, "-o", path) run(true, "verify-bundle", "-f", path, "-pubkey-base64", srv.BundlePublicKeyBase64()) raw, err := os.ReadFile(path) if err != nil { t.Fatal(err) } pub, err := base64.StdEncoding.DecodeString(srv.BundlePublicKeyBase64()) if err != nil { t.Fatal(err) } verified, err := bundle.VerifyGzippedTar(raw, ed25519.PublicKey(pub)) if err != nil || verified.Manifest.RevisionID != revision || verified.Manifest.SpeakerID != speaker { t.Fatalf("legacy CLI fetched the wrong revision/speaker or an invalid bundle: %v", err) } // The archive is valid, but verification with an unrelated key must fail. run(false, "verify-bundle", "-f", path, "-pubkey-hex", strings.Repeat("00", ed25519.PublicKeySize)) for _, token := range []string{"invalid-node", "other-node"} { denied := filepath.Join(t.TempDir(), "denied.tar.gz") run(false, "pull-bundle", "-base-url", server.URL, "-token", token, "-speaker-id", speaker, "-o", denied) if _, err := os.Stat(denied); !os.IsNotExist(err) { t.Fatalf("unauthorized CLI wrote a bundle: %v", err) } } // A damaged payload must not pass the old verifier with the correct key. if err := os.WriteFile(path, []byte("not a signed bundle"), 0o600); err != nil { t.Fatal(err) } run(false, "verify-bundle", "-f", path, "-pubkey-base64", srv.BundlePublicKeyBase64()) }