package apigen import ( "bytes" "encoding/json" apierrors "git.yuntuo.hk/k2safe/go-omni/errors" "os" "path/filepath" "strings" "testing" ) func TestControlledSharedContracts(t *testing.T) { root := t.TempDir() shared := filepath.Join(root, "shared") writeFile(t, filepath.Join(shared, "response.api"), "response Common (codeType: int32; successCode: 0; errorCode: 9999; dataMode: always)\ntype Item { Name string `json:\"name\"` }\n") for _, service := range []string{"api", "id"} { dir := filepath.Join(root, service) entry := filepath.Join(dir, "api/service.api") writeFile(t, entry, "import \"@common/response.api\"\n@app(defaultResponse: Common)\nservice demo {\n@handler get\nget /get returns (Item)\n}\n") config := `{"roots":{"common":{"path":"../../shared","version":"1.0.0"}}}` writeFile(t, filepath.Join(dir, "api/omni.imports.json"), config) spec, err := Parse(entry) if err != nil { t.Fatal(err) } if len(spec.Sources) != 2 || len(spec.SourcePaths) != 2 { t.Fatalf("%+v", spec) } pin := sharedHash(spec.Sources, "common") config = strings.Replace(config, `"version":"1.0.0"`, `"version":"1.0.0","sha256":"`+pin+`"`, 1) writeFile(t, filepath.Join(dir, "api/omni.imports.json"), config) o := Options{Dir: dir, API: entry, Module: "example.com/" + service, FrameworkDir: frameworkRoot(t)} if _, err = Generate(o); err != nil { t.Fatal(err) } o.Check = true if _, err = Generate(o); err != nil { t.Fatal(err) } if _, err = os.Stat(filepath.Join(dir, "api/@common")); !os.IsNotExist(err) { t.Fatal("shared contract copied into service") } document := readFile(t, filepath.Join(dir, "docs/openapi.json")) if !strings.Contains(document, pin) || !strings.Contains(document, "@common/response.api") { t.Fatal("missing provenance") } writeFile(t, filepath.Join(dir, "api/omni.imports.json"), strings.Replace(config, pin, strings.Repeat("0", 64), 1)) if _, err = Parse(entry); err == nil { t.Fatal("checksum mismatch accepted") } } // An undeclared alias, traversal, symlink escape and cycle are all rejected. for _, imp := range []string{"@missing/response.api", "@common/../outside.api", "../shared/response.api", "@common/escape.api", "@common/cycle.api"} { local := filepath.Join(root, "consumer") writeFile(t, filepath.Join(local, "omni.imports.json"), `{"roots":{"common":{"path":"../shared"}}}`) writeFile(t, filepath.Join(root, "outside.api"), "type Outside {}\n") _ = os.Symlink(filepath.Join(root, "outside.api"), filepath.Join(shared, "escape.api")) writeFile(t, filepath.Join(shared, "cycle.api"), "import \"cycle.api\"\n") entry := filepath.Join(local, "service.api") writeFile(t, entry, "import "+strconvQuote(imp)+"\nservice demo{\n@handler get\nget /get\n}\n") if _, err := Parse(entry); err == nil { t.Fatal("unsafe import accepted", imp) } } } func strconvQuote(value string) string { data, _ := json.Marshal(value); return string(data) } func TestCatalogDocumentExportDoesNotGenerateRuntimeRegistry(t *testing.T) { dir := t.TempDir() entry := filepath.Join(dir, "service.api") writeFile(t, entry, "@app(defaultResponse: Common)\nresponse Common(codeType: int32; successCode: 0; errorCode: 9999; messageField: msg; dataMode: always)\nservice demo{\n@handler get\nget /get returns (null)\n}\n") var exported bytes.Buffer err := apierrors.ExportCatalog(&exported, apierrors.Catalog{DefaultStatus: 200, Definitions: map[string]apierrors.Definition{"TOO_LONG": {Code: 400002, Message: "{label}不能超过{max}个字符", Params: []apierrors.Parameter{{Name: "label", Type: "string", Example: "企业名称"}, {Name: "max", Type: "int", Example: 100}}}}}) if err != nil { t.Fatal(err) } writeFile(t, filepath.Join(dir, "omni.errors.json"), exported.String()) spec, err := Parse(entry) if err != nil { t.Fatal(err) } c, err := Compile(spec) if err != nil { t.Fatal(err) } if len(c.Spec.Errors) != 0 || !strings.Contains(string(c.Document), "企业名称不能超过100个字符") { t.Fatal(string(c.Document)) } target := filepath.Join(t.TempDir(), "catalog") if _, err = Generate(Options{Dir: target, API: entry, Module: "example.com/catalog", FrameworkDir: frameworkRoot(t)}); err != nil { t.Fatal(err) } if _, err = os.Stat(filepath.Join(target, "internal/apierrors/errorsGen.go")); !os.IsNotExist(err) { t.Fatal("document metadata became a runtime error registry") } }