162 lines
5.7 KiB
Go
162 lines
5.7 KiB
Go
package apigen
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCompatibilityContract(t *testing.T) {
|
|
s, err := Parse(filepath.Join(frameworkRoot(t), "examples/contracts/compatibility.api"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c, err := Compile(s)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(s.Scalars) != 1 || len(s.Routes) != 6 {
|
|
t.Fatal("incomplete contract")
|
|
}
|
|
if !strings.Contains(string(c.Document), `"anyOf"`) || !strings.Contains(string(c.Document), `"multipart/form-data"`) {
|
|
t.Fatal("missing schemas")
|
|
}
|
|
for _, change := range [][2]string{{"jsonInput: string | int64", "jsonInput: mystery"}, {"text: true", "text: false"}, {"mode: multipart", "mode: invalid"}, {"maxBytes: 128", "maxBytes: 0"}, {"goType: ID", "goType: id"}, {"Data any", "Data interface{ Method() }"}} {
|
|
source := readFile(t, filepath.Join(frameworkRoot(t), "examples/contracts/compatibility.api"))
|
|
spec, err := parseText(t, strings.Replace(source, change[0], change[1], 1))
|
|
if err == nil {
|
|
_, err = Compile(spec)
|
|
}
|
|
if err == nil {
|
|
t.Fatalf("accepted %s", change[1])
|
|
}
|
|
}
|
|
}
|
|
func TestCompatibilityConsumer(t *testing.T) {
|
|
if os.Getenv("GO_OMNI_SCAFFOLD_INTEGRATION") == "" {
|
|
t.Skip("consumer integration")
|
|
}
|
|
root := frameworkRoot(t)
|
|
dir := filepath.Join(t.TempDir(), "consumer")
|
|
o := Options{API: filepath.Join(root, "examples/contracts/compatibility.api"), Dir: dir, Module: "example.com/compatibility", FrameworkDir: root}
|
|
releaseCLI := os.Getenv("GO_OMNI_RELEASE_CLI")
|
|
generate := func(check bool) {
|
|
t.Helper()
|
|
if releaseCLI == "" {
|
|
options := o
|
|
options.Check = check
|
|
if _, err := Generate(options); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
args := []string{"api", "generate", "--api", o.API, "--dir", dir, "--module", o.Module}
|
|
if check {
|
|
args = append(args, "--check")
|
|
}
|
|
cmd := exec.CommandContext(ctx, releaseCLI, args...)
|
|
cmd.Dir = filepath.Dir(dir)
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("released CLI: %v\n%s", err, output)
|
|
}
|
|
}
|
|
generate(false)
|
|
files := map[string]string{
|
|
"internal/logic/demo/echo.go": "echo.go",
|
|
"internal/handler/demo/customBind.go": "hooks.go",
|
|
"internal/handler/files/downloadManual.go": "download.go",
|
|
"consumer_test.go": "consumer_test.go",
|
|
}
|
|
// The fixture implements related methods together; remove only its generated
|
|
// one-time stubs, then fill every stub so regeneration can preserve all files.
|
|
for _, name := range []string{"internal/logic/demo/item.go", "internal/logic/demo/custom.go"} {
|
|
writeFile(t, filepath.Join(dir, name), "package demo\n")
|
|
}
|
|
for _, name := range []string{"internal/handler/demo/uploadManual.go", "internal/handler/demo/callbackManual.go"} {
|
|
writeFile(t, filepath.Join(dir, name), "package demo\n")
|
|
}
|
|
for target, source := range files {
|
|
writeFile(t, filepath.Join(dir, target), readFile(t, filepath.Join("testdata/compatibility", source)))
|
|
}
|
|
before := map[string]string{}
|
|
for target := range files {
|
|
before[target] = readFile(t, filepath.Join(dir, target))
|
|
}
|
|
generate(false)
|
|
for target, content := range before {
|
|
if readFile(t, filepath.Join(dir, target)) != content {
|
|
t.Fatalf("overwrote %s", target)
|
|
}
|
|
}
|
|
generate(true)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
|
defer cancel()
|
|
cmd := exec.CommandContext(ctx, "go", "test", "-mod=mod", "-race", "-count=1", "./...")
|
|
cmd.Dir = dir
|
|
cmd.Env = append(os.Environ(), "GOWORK=off")
|
|
if output, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("consumer: %v\n%s", err, output)
|
|
}
|
|
if releaseCLI != "" {
|
|
cmd = exec.CommandContext(ctx, "go", "list", "-m", "-json", frameworkModule)
|
|
cmd.Dir = dir
|
|
cmd.Env = append(os.Environ(), "GOWORK=off")
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("module: %v\n%s", err, output)
|
|
}
|
|
var module struct {
|
|
Version, Sum string
|
|
Replace any
|
|
}
|
|
if err = json.Unmarshal(output, &module); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if module.Version != os.Getenv("GO_OMNI_RELEASE_VERSION") || module.Replace != nil || module.Sum == "" {
|
|
t.Fatalf("not a fixed remote dependency: %+v", module)
|
|
}
|
|
t.Logf("remote framework %s checksum %s; no local replace", module.Version, module.Sum)
|
|
}
|
|
|
|
}
|
|
|
|
func TestNestedDynamicAliases(t *testing.T) {
|
|
source := readFile(t, filepath.Join(frameworkRoot(t), "examples/contracts/compatibility.api"))
|
|
for _, typ := range []string{"[]interface{}", "map[string]interface{}", "map[string][]interface{}"} {
|
|
s, err := parseText(t, strings.Replace(source, "Data any", "Data "+typ, 1))
|
|
if err == nil {
|
|
_, err = Compile(s)
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", typ, err)
|
|
}
|
|
}
|
|
}
|
|
func TestSwitchFromManualRequiresExplicitMigration(t *testing.T) {
|
|
root := frameworkRoot(t)
|
|
o := Options{API: filepath.Join(root, "examples/contracts/compatibility.api"), Dir: filepath.Join(t.TempDir(), "consumer"), Module: "example.com/compatibility", FrameworkDir: root}
|
|
if _, err := Generate(o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The hand-written source may contain business code. Never delete it to make
|
|
// the newly automatic handler compile; fail before touching generated files.
|
|
o.API = filepath.Join(o.Dir, "api/compatibility.api")
|
|
source := readFile(t, o.API)
|
|
source = strings.Replace(source, "@handlerMode manual\n @produces \"application/octet-stream\"", "", 1)
|
|
writeFile(t, o.API, source)
|
|
before := snapshot(t, o.Dir)
|
|
if _, err := Generate(o); err == nil || !strings.Contains(err.Error(), "migrate and remove") {
|
|
t.Fatalf("unsafe mode switch: %v", err)
|
|
}
|
|
if !sameFiles(before, snapshot(t, o.Dir)) {
|
|
t.Fatal("failed mode switch mutated project")
|
|
}
|
|
}
|