mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-04 08:30:48 +02:00
9e031eb3df
Add a build-time conversion step that transforms the existing Swagger 2.0 spec into an OpenAPI 3.0 spec. The OAS3 spec is served alongside the existing Swagger 2.0 spec, enabling API clients that require OAS3 to generate code directly from Gitea's API. This is not to be an answer to how gitea handles OAS3 long term, but a way to use what we have to move a step forward. --------- Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
171 lines
5.2 KiB
Go
171 lines
5.2 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package openapi3gen
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/getkin/kin-openapi/openapi3"
|
|
)
|
|
|
|
func TestDeriveEnumName_hit(t *testing.T) {
|
|
key := EnumKey([]any{"red", "green", "blue"})
|
|
astMap := map[string]string{key: "Color"}
|
|
usages := []enumUsage{{schemaName: "Paint", propName: "color"}}
|
|
got, err := deriveEnumName(key, usages, astMap)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got != "Color" {
|
|
t.Fatalf("got %q, want %q", got, "Color")
|
|
}
|
|
}
|
|
|
|
func TestDeriveEnumName_miss(t *testing.T) {
|
|
key := EnumKey([]any{"x", "y"})
|
|
usages := []enumUsage{{schemaName: "Thing", propName: "kind"}}
|
|
_, err := deriveEnumName(key, usages, map[string]string{})
|
|
if err == nil {
|
|
t.Fatal("expected miss error, got nil")
|
|
}
|
|
msg := err.Error()
|
|
if !strings.Contains(msg, "Thing.kind") {
|
|
t.Fatalf("error %q should list the missing usage", msg)
|
|
}
|
|
if !strings.Contains(msg, "swagger:enum") {
|
|
t.Fatalf("error %q should hint at the fix", msg)
|
|
}
|
|
}
|
|
|
|
func TestExtractSharedEnums_usesASTMap(t *testing.T) {
|
|
doc := &openapi3.T{
|
|
Components: &openapi3.Components{
|
|
Schemas: openapi3.Schemas{
|
|
"A": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"object"},
|
|
Properties: openapi3.Schemas{
|
|
"color": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"string"},
|
|
Enum: []any{"red", "green", "blue"},
|
|
}},
|
|
},
|
|
}},
|
|
"B": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"object"},
|
|
Properties: openapi3.Schemas{
|
|
"color": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"string"},
|
|
Enum: []any{"red", "green", "blue"},
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
astMap := map[string]string{EnumKey([]any{"red", "green", "blue"}): "Color"}
|
|
if err := extractSharedEnums(doc, astMap); err != nil {
|
|
t.Fatalf("extractSharedEnums: %v", err)
|
|
}
|
|
if _, ok := doc.Components.Schemas["Color"]; !ok {
|
|
t.Fatalf("expected Color schema to be extracted")
|
|
}
|
|
}
|
|
|
|
func TestFixFileSchemas_recursesIntoNested(t *testing.T) {
|
|
fileType := func() *openapi3.SchemaRef {
|
|
return &openapi3.SchemaRef{Value: &openapi3.Schema{Type: &openapi3.Types{"file"}}}
|
|
}
|
|
doc := &openapi3.T{
|
|
Paths: openapi3.NewPaths(),
|
|
}
|
|
doc.Paths.Set("/upload", &openapi3.PathItem{
|
|
Post: &openapi3.Operation{
|
|
RequestBody: &openapi3.RequestBodyRef{
|
|
Value: &openapi3.RequestBody{
|
|
Content: openapi3.Content{
|
|
"multipart/form-data": {
|
|
Schema: &openapi3.SchemaRef{Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"object"},
|
|
Properties: openapi3.Schemas{
|
|
"attachment": fileType(),
|
|
"items": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"array"},
|
|
Items: fileType(),
|
|
}},
|
|
"alt": {Value: &openapi3.Schema{
|
|
AllOf: openapi3.SchemaRefs{fileType()},
|
|
}},
|
|
"one": {Value: &openapi3.Schema{
|
|
OneOf: openapi3.SchemaRefs{fileType()},
|
|
}},
|
|
"any": {Value: &openapi3.Schema{
|
|
AnyOf: openapi3.SchemaRefs{fileType()},
|
|
}},
|
|
"not": {Value: &openapi3.Schema{
|
|
Not: fileType(),
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Responses: openapi3.NewResponses(),
|
|
},
|
|
})
|
|
|
|
fixFileSchemas(doc)
|
|
|
|
props := doc.Paths.Value("/upload").Post.RequestBody.Value.Content["multipart/form-data"].Schema.Value.Properties
|
|
if !props["attachment"].Value.Type.Is("string") || props["attachment"].Value.Format != "binary" {
|
|
t.Errorf("nested property not fixed: %+v", props["attachment"].Value)
|
|
}
|
|
if !props["items"].Value.Items.Value.Type.Is("string") || props["items"].Value.Items.Value.Format != "binary" {
|
|
t.Errorf("array items not fixed: %+v", props["items"].Value.Items.Value)
|
|
}
|
|
if !props["alt"].Value.AllOf[0].Value.Type.Is("string") || props["alt"].Value.AllOf[0].Value.Format != "binary" {
|
|
t.Errorf("allOf branch not fixed: %+v", props["alt"].Value.AllOf[0].Value)
|
|
}
|
|
if !props["one"].Value.OneOf[0].Value.Type.Is("string") || props["one"].Value.OneOf[0].Value.Format != "binary" {
|
|
t.Errorf("oneOf branch not fixed: %+v", props["one"].Value.OneOf[0].Value)
|
|
}
|
|
if !props["any"].Value.AnyOf[0].Value.Type.Is("string") || props["any"].Value.AnyOf[0].Value.Format != "binary" {
|
|
t.Errorf("anyOf branch not fixed: %+v", props["any"].Value.AnyOf[0].Value)
|
|
}
|
|
if !props["not"].Value.Not.Value.Type.Is("string") || props["not"].Value.Not.Value.Format != "binary" {
|
|
t.Errorf("not branch not fixed: %+v", props["not"].Value.Not.Value)
|
|
}
|
|
}
|
|
|
|
func TestExtractSharedEnums_missReturnsError(t *testing.T) {
|
|
doc := &openapi3.T{
|
|
Components: &openapi3.Components{
|
|
Schemas: openapi3.Schemas{
|
|
"A": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"object"},
|
|
Properties: openapi3.Schemas{
|
|
"color": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"string"},
|
|
Enum: []any{"red", "green"},
|
|
}},
|
|
},
|
|
}},
|
|
"B": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"object"},
|
|
Properties: openapi3.Schemas{
|
|
"color": {Value: &openapi3.Schema{
|
|
Type: &openapi3.Types{"string"},
|
|
Enum: []any{"red", "green"},
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
}
|
|
if err := extractSharedEnums(doc, map[string]string{}); err == nil {
|
|
t.Fatal("expected miss error")
|
|
}
|
|
}
|