@@ -8,13 +8,13 @@ import (
88 "errors"
99 "os"
1010 "path/filepath"
11- "reflect"
1211 "sort"
1312 "strconv"
1413 "strings"
1514 "testing"
1615
1716 "github.com/cipherstash/stack/languages/golang/encrypt"
17+ "github.com/cipherstash/stack/languages/golang/internal/record"
1818 "github.com/cipherstash/vitaminc/bindings/go/vcffi"
1919 "github.com/cipherstash/vitaminc/bindings/go/vcvalue"
2020)
@@ -61,17 +61,7 @@ func declarationValue(t *testing.T, value any) any {
6161 }
6262 return v
6363 case json.Number :
64- text := v .String ()
65- var value any
66- var err error
67- switch {
68- case strings .ContainsAny (text , ".eE" ):
69- value , err = strconv .ParseFloat (text , 64 )
70- case strings .HasPrefix (text , "-" ):
71- value , err = strconv .ParseInt (text , 10 , 64 )
72- default :
73- value , err = strconv .ParseUint (text , 10 , 64 )
74- }
64+ value , err := declarationNumber (v .String ())
7565 if err != nil {
7666 t .Fatal (err )
7767 }
@@ -92,21 +82,44 @@ func TestDeclarationConformance(t *testing.T) {
9282 if corpus .Version != 1 || corpus .Profile != "record-declarations-v1" || corpus .Resolver != "none" || len (corpus .Cases ) == 0 {
9383 t .Fatal ("unsupported or empty declaration corpus" )
9484 }
95- wasm , err := os .ReadFile ("testdata/stack_encrypt_guest_deterministic.wasm" )
96- if errors .Is (err , os .ErrNotExist ) && os .Getenv ("STACK_CONFORMANCE_REQUIRED" ) != "1" {
97- t .Skip (err )
98- }
99- if err != nil {
100- t .Fatal (err )
85+ var hostPlans map [string ]record.Plan
86+ readConformanceJSON (t , filepath .Join (".." , ".." , ".." , "conformance" , "stack-encrypt" , "v1" , "go-declarations.json" ), & hostPlans )
87+ for name , load := range map [string ]func () ([]byte , error ){"release" : encrypt .PlainGuest , "deterministic" : encrypt .DeterministicGuest } {
88+ t .Run (name , func (t * testing.T ) {
89+ wasm , err := load ()
90+ if (errors .Is (err , encrypt .ErrGuestNotBuilt ) || errors .Is (err , encrypt .ErrDeterministicGuestNotBuilt )) && os .Getenv ("STACK_CONFORMANCE_REQUIRED" ) != "1" {
91+ t .Skip (err )
92+ }
93+ if err != nil {
94+ t .Fatal (err )
95+ }
96+ checker , err := encrypt .NewCheckerOver (context .Background (), wasm )
97+ if err != nil {
98+ t .Fatal (err )
99+ }
100+ t .Cleanup (func () { _ = checker .Close () })
101+ checkDeclarationCases (t , checker , corpus .Cases , hostPlans )
102+ })
101103 }
104+ }
105+
106+ func checkDeclarationCases (t * testing.T , checker * encrypt.Checker , cases []declarationCase , hostPlans map [string ]record.Plan ) {
107+ t .Helper ()
102108 ctx := context .Background ()
103- checker , err := encrypt .NewCheckerOver (ctx , wasm )
104- if err != nil {
105- t .Fatal (err )
109+ for id := range hostPlans {
110+ found := false
111+ for _ , c := range cases {
112+ if c .ID == id {
113+ found = true
114+ break
115+ }
116+ }
117+ if ! found {
118+ t .Fatalf ("host plan has no shared case: %s" , id )
119+ }
106120 }
107- t .Cleanup (func () { _ = checker .Close () })
108121 seen := map [string ]bool {}
109- for _ , c := range corpus . Cases {
122+ for _ , c := range cases {
110123 if c .ID == "" || seen [c .ID ] {
111124 t .Fatalf ("empty or duplicate case ID %q" , c .ID )
112125 }
@@ -138,6 +151,15 @@ func TestDeclarationConformance(t *testing.T) {
138151 if err != nil {
139152 t .Fatal (err )
140153 }
154+ if plan , ok := hostPlans [c .ID ]; ok {
155+ hostErr := checker .Check (ctx , & plan )
156+ if (hostErr == nil ) != accepted {
157+ t .Fatalf ("host Check: %v; accepted=%v" , hostErr , accepted )
158+ }
159+ if hostErr != nil && ! errors .Is (hostErr , encrypt .ErrEncoding ) {
160+ t .Fatalf ("host Check: expected ErrEncoding, got %v" , hostErr )
161+ }
162+ }
141163 err = checker .CheckFrameForTest (ctx , frame )
142164 expected := c .ExpectedError
143165 if c .BoundaryError != nil {
@@ -159,9 +181,55 @@ func TestDeclarationConformance(t *testing.T) {
159181 if ! errors .Is (err , encrypt .ErrEncoding ) || ! errors .As (err , & diagnostic ) {
160182 t .Fatalf ("expected encoding diagnostic, got %v" , err )
161183 }
162- if diagnostic .Code != expected .Code || ! reflect . DeepEqual ( diagnostic .Fields , expected .Fields ) {
184+ if diagnostic .Code != expected .Code || ! sameDiagnosticFields ( t , diagnostic .Fields , expected .Fields ) {
163185 t .Fatalf ("got %s %#v; expected %s %#v" , diagnostic .Code , diagnostic .Fields , expected .Code , expected .Fields )
164186 }
165187 })
166188 }
167189}
190+
191+ // Compare JSON payload values without float64 rounding or int64/uint64 type
192+ // accidents. UseNumber in readConformanceJSON preserves expected integers.
193+ func sameDiagnosticFields (t * testing.T , actual , expected map [string ]any ) bool {
194+ t .Helper ()
195+ a , err := json .Marshal (actual )
196+ if err != nil {
197+ t .Fatal (err )
198+ }
199+ b , err := json .Marshal (expected )
200+ if err != nil {
201+ t .Fatal (err )
202+ }
203+ return bytes .Equal (a , b )
204+ }
205+
206+ func TestDiagnosticNumericFields (t * testing.T ) {
207+ if ! sameDiagnosticFields (t , map [string ]any {"n" : uint64 (18446744073709551615 ), "m" : int64 (- 1 )}, map [string ]any {"n" : json .Number ("18446744073709551615" ), "m" : json .Number ("-1" )}) {
208+ t .Fatal ("integer payload changed" )
209+ }
210+ if sameDiagnosticFields (t , map [string ]any {"n" : uint64 (18446744073709551615 )}, map [string ]any {"n" : json .Number ("18446744073709551614" )}) {
211+ t .Fatal ("integer precision lost" )
212+ }
213+ }
214+
215+ func declarationNumber (text string ) (any , error ) {
216+ switch {
217+ case strings .ContainsAny (text , ".eE" ):
218+ return strconv .ParseFloat (text , 64 )
219+ case strings .HasPrefix (text , "-" ):
220+ return strconv .ParseInt (text , 10 , 64 )
221+ default :
222+ return strconv .ParseUint (text , 10 , 64 )
223+ }
224+ }
225+ func TestDeclarationNumberProjection (t * testing.T ) {
226+ zero , err := declarationNumber ("-0" )
227+ if err != nil || zero != int64 (0 ) {
228+ t .Fatalf ("-0: %v %v" , zero , err )
229+ }
230+ for _ , invalid := range []string {"18446744073709551616" , "-9223372036854775809" , "1e400" } {
231+ if _ , err := declarationNumber (invalid ); err == nil {
232+ t .Fatalf ("accepted %s" , invalid )
233+ }
234+ }
235+ }
0 commit comments