// 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) } }