encoding/openapi: finalise when constructing openapi AST

When building OpenAPI enums from a CUE instance, specifically one that
contains an enum imported from protobuf, we don't care about the
definition-encoded integer enum value (introduced as part of dcfff000).
Instead we only care about the final value of the enum.

Prior to dcfff000, an imported protobuf enum would look like this:

    #CaptureMode: "DEFAULT" | "IPTABLES" | "NONE"

Post dcfff000, it looks like:

    #CaptureMode: {
            "DEFAULT"
            #enumValue: 0
    } | {
            "IPTABLES"
            #enumValue: 1
    } | {
            "NONE"
            #enumValue: 2
    }

Therefore, in the process of building an enum we simply need to finalise
the CUE Value prior to deriving its syntax.

The test as part of this CL comes from a real Istio schema that
uncovered this bug in the first place.

Fixes #977

Change-Id: I5b4cbb2f35b0535488d8f77b1e2166f89ed2644d
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9843
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index d43490c..b7a958d 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -1301,10 +1301,10 @@
 
 func (b *builder) decode(v cue.Value) ast.Expr {
 	v, _ = v.Default()
-	return v.Syntax().(ast.Expr)
+	return v.Syntax(cue.Final()).(ast.Expr)
 }
 
 func (b *builder) big(v cue.Value) ast.Expr {
 	v, _ = v.Default()
-	return v.Syntax().(ast.Expr)
+	return v.Syntax(cue.Final()).(ast.Expr)
 }
diff --git a/encoding/openapi/openapi_test.go b/encoding/openapi/openapi_test.go
index 1575933..8e93a80 100644
--- a/encoding/openapi/openapi_test.go
+++ b/encoding/openapi/openapi_test.go
@@ -64,6 +64,10 @@
 		"array.json",
 		defaultConfig,
 	}, {
+		"enum.cue",
+		"enum.json",
+		defaultConfig,
+	}, {
 		"struct.cue",
 		"struct.json",
 		defaultConfig,
@@ -140,6 +144,12 @@
 				t.Fatal(errors.Details(inst.Err, nil))
 			}
 
+			all, err := tc.config.All(inst)
+			if err != nil {
+				t.Fatal(err)
+			}
+			walk(all)
+
 			b, err := openapi.Gen(inst, tc.config)
 			if err != nil {
 				t.Fatal(err)
@@ -165,6 +175,21 @@
 	}
 }
 
+// walk traverses an openapi.OrderedMap. This is a helper function
+// used to ensure that a generated OpenAPI value is well-formed.
+func walk(om *openapi.OrderedMap) {
+	for _, p := range om.Pairs() {
+		switch p := p.Value.(type) {
+		case *openapi.OrderedMap:
+			walk(p)
+		case []*openapi.OrderedMap:
+			for _, om := range p {
+				walk(om)
+			}
+		}
+	}
+}
+
 // This is for debugging purposes. Do not remove.
 func TestX(t *testing.T) {
 	t.Skip()
diff --git a/encoding/openapi/testdata/enum.cue b/encoding/openapi/testdata/enum.cue
new file mode 100644
index 0000000..237ff5e
--- /dev/null
+++ b/encoding/openapi/testdata/enum.cue
@@ -0,0 +1,10 @@
+#CaptureMode: {
+	"DEFAULT"
+	#enumValue: 0
+} | {
+	"IPTABLES"
+	#enumValue: 1
+} | {
+	"NONE"
+	#enumValue: 2
+}
diff --git a/encoding/openapi/testdata/enum.json b/encoding/openapi/testdata/enum.json
new file mode 100644
index 0000000..81ca198
--- /dev/null
+++ b/encoding/openapi/testdata/enum.json
@@ -0,0 +1,20 @@
+{
+   "openapi": "3.0.0",
+   "info": {
+      "title": "Generated by cue.",
+      "version": "no version"
+   },
+   "paths": {},
+   "components": {
+      "schemas": {
+         "CaptureMode": {
+            "type": "string",
+            "enum": [
+               "DEFAULT",
+               "IPTABLES",
+               "NONE"
+            ]
+         }
+      }
+   }
+}
\ No newline at end of file