debian-forge-composer/cmd/mock-dnf-json/dnf-json.go
2024-02-20 15:55:47 +01:00

116 lines
2.7 KiB
Go

// Mock dnf-json
//
// The purpose of this program is to return fake but expected responses to
// dnf-json depsolve and dump queries. Tests should initialise a
// dnfjson.Solver and configure it to run this program via the SetDNFJSONPath()
// method. This utility accepts queries and returns responses with the same
// structure as the dnf-json Python script.
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/osbuild/images/pkg/dnfjson"
)
func maybeFail(err error) {
if err != nil {
fail(err)
}
}
func fail(err error) {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
func readRequest(r io.Reader) dnfjson.Request {
j := json.NewDecoder(os.Stdin)
j.DisallowUnknownFields()
var req dnfjson.Request
err := j.Decode(&req)
maybeFail(err)
return req
}
func readTestCase() string {
if len(os.Args) < 2 {
fail(errors.New("no test case specified"))
}
if len(os.Args) > 2 {
fail(errors.New("invalid number of arguments: you must specify a test case"))
}
return os.Args[1]
}
func parseResponse(resp []byte, req dnfjson.Request) json.RawMessage {
parsedResponse := make(map[string]json.RawMessage)
err := json.Unmarshal(resp, &parsedResponse)
maybeFail(err)
if req.Command == "search" {
// Search requests need to return results based on the search
// The key to the search is a comma-separated list of the requested packages
key := strings.Join(req.Arguments.Search.Packages, ",")
// Extract the possible response map
var searches map[string]json.RawMessage
err = json.Unmarshal(parsedResponse["search"], &searches)
maybeFail(err)
if _, ok := searches[key]; !ok {
fail(fmt.Errorf("search response map is missing key = %s", key))
}
return searches[key]
} else {
return parsedResponse[req.Command]
}
}
func checkForError(msg json.RawMessage) bool {
j := json.NewDecoder(bytes.NewReader(msg))
j.DisallowUnknownFields()
dnferror := new(dnfjson.Error)
err := j.Decode(dnferror)
return err == nil
}
func main() {
testFilePath := readTestCase()
req := readRequest(os.Stdin)
testFile, err := os.Open(testFilePath)
if err != nil {
fail(fmt.Errorf("failed to open test file %q\n", testFilePath))
}
defer testFile.Close()
response, err := io.ReadAll(testFile)
if err != nil {
fail(fmt.Errorf("failed to read test file %q\n", testFilePath))
}
res := parseResponse(response, req)
if req.Command == "depsolve" {
// add repo ID to packages
// just use the first
for _, repo := range req.Arguments.Repos {
res = bytes.ReplaceAll(res, []byte("REPOID"), []byte(repo.ID))
break
}
}
fmt.Print(string(res))
// check if we should return with error
if checkForError(res) {
os.Exit(1)
}
}