@@ -19,14 +19,57 @@ import (
1919 "os/exec"
2020 "path"
2121 "path/filepath"
22+ "slices"
23+ "strings"
2224 "testing"
2325
2426 "github.com/googleapis/librarian/internal/sidekick/internal/config"
2527 "github.com/googleapis/librarian/internal/sidekick/internal/parser"
2628)
2729
2830var (
29- testdataDir , _ = filepath .Abs ("../../testdata" )
31+ testdataDir , _ = filepath .Abs ("../../testdata" )
32+ expectedInNosvc = []string {
33+ "README.md" ,
34+ "Cargo.toml" ,
35+ path .Join ("src" , "lib.rs" ),
36+ path .Join ("src" , "model.rs" ),
37+ path .Join ("src" , "model" , "debug.rs" ),
38+ path .Join ("src" , "model" , "deserialize.rs" ),
39+ path .Join ("src" , "model" , "serialize.rs" ),
40+ }
41+ expectedInCrate = append (expectedInNosvc ,
42+ path .Join ("src" , "builder.rs" ),
43+ path .Join ("src" , "client.rs" ),
44+ path .Join ("src" , "tracing.rs" ),
45+ path .Join ("src" , "transport.rs" ),
46+ path .Join ("src" , "stub.rs" ),
47+ path .Join ("src" , "stub" , "dynamic.rs" ),
48+ )
49+ expectedInClient = []string {
50+ path .Join ("mod.rs" ),
51+ path .Join ("model.rs" ),
52+ path .Join ("model" , "debug.rs" ),
53+ path .Join ("model" , "deserialize.rs" ),
54+ path .Join ("model" , "serialize.rs" ),
55+ path .Join ("builder.rs" ),
56+ path .Join ("client.rs" ),
57+ path .Join ("tracing.rs" ),
58+ path .Join ("transport.rs" ),
59+ path .Join ("stub.rs" ),
60+ path .Join ("stub" , "dynamic.rs" ),
61+ }
62+ unexpectedInClient = []string {
63+ "README.md" ,
64+ "Cargo.toml" ,
65+ path .Join ("src" , "lib.rs" ),
66+ }
67+ expectedInModule = []string {
68+ path .Join ("mod.rs" ),
69+ path .Join ("debug.rs" ),
70+ path .Join ("deserialize.rs" ),
71+ path .Join ("serialize.rs" ),
72+ }
3073)
3174
3275func TestRustFromOpenAPI (t * testing.T ) {
@@ -47,7 +90,7 @@ func TestRustFromOpenAPI(t *testing.T) {
4790 if err := Generate (model , outDir , cfg ); err != nil {
4891 t .Fatal (err )
4992 }
50- for _ , expected := range [] string { "README.md" , "Cargo.toml" , "src/lib.rs" } {
93+ for _ , expected := range expectedInCrate {
5194 filename := path .Join (outDir , expected )
5295 stat , err := os .Stat (filename )
5396 if os .IsNotExist (err ) {
@@ -57,6 +100,7 @@ func TestRustFromOpenAPI(t *testing.T) {
57100 t .Errorf ("generated files should not be executable %s: %o" , filename , stat .Mode ())
58101 }
59102 }
103+ importsModelModules (t , path .Join (outDir , "src" , "model.rs" ))
60104}
61105
62106func TestRustFromProtobuf (t * testing.T ) {
@@ -80,7 +124,91 @@ func TestRustFromProtobuf(t *testing.T) {
80124 if err := Generate (model , outDir , cfg ); err != nil {
81125 t .Fatal (err )
82126 }
83- for _ , expected := range []string {"README.md" , "Cargo.toml" , "src/lib.rs" } {
127+ for _ , expected := range expectedInCrate {
128+ filename := path .Join (outDir , expected )
129+ stat , err := os .Stat (filename )
130+ if os .IsNotExist (err ) {
131+ t .Errorf ("missing %s: %s" , filename , err )
132+ }
133+ if stat .Mode ().Perm ()| 0666 != 0666 {
134+ t .Errorf ("generated files should not be executable %s: %o" , filename , stat .Mode ())
135+ }
136+ }
137+ importsModelModules (t , path .Join (outDir , "src" , "model.rs" ))
138+ }
139+
140+ func TestRustClient (t * testing.T ) {
141+ requireProtoc (t )
142+ for _ , override := range []string {"http-client" , "grpc-client" } {
143+ outDir := t .TempDir ()
144+
145+ cfg := & config.Config {
146+ General : config.GeneralConfig {
147+ SpecificationFormat : "protobuf" ,
148+ ServiceConfig : "google/cloud/secretmanager/v1/secretmanager_v1.yaml" ,
149+ SpecificationSource : "google/cloud/secretmanager/v1" ,
150+ },
151+ Source : map [string ]string {
152+ "googleapis-root" : path .Join (testdataDir , "googleapis" ),
153+ },
154+ Codec : map [string ]string {
155+ "copyright-year" : "2025" ,
156+ "template-override" : path .Join ("templates" , override ),
157+ },
158+ }
159+ model , err := parser .CreateModel (cfg )
160+ if err != nil {
161+ t .Fatal (err )
162+ }
163+ if err := Generate (model , outDir , cfg ); err != nil {
164+ t .Fatal (err )
165+ }
166+ for _ , expected := range expectedInClient {
167+ filename := path .Join (outDir , expected )
168+ stat , err := os .Stat (filename )
169+ if os .IsNotExist (err ) {
170+ t .Errorf ("missing %s: %s" , filename , err )
171+ }
172+ if stat .Mode ().Perm ()| 0666 != 0666 {
173+ t .Errorf ("generated files should not be executable %s: %o" , filename , stat .Mode ())
174+ }
175+ }
176+ for _ , unexpected := range unexpectedInClient {
177+ filename := path .Join (outDir , unexpected )
178+ if stat , err := os .Stat (filename ); err == nil {
179+ t .Errorf ("did not expect file %s, got=%v" , unexpected , stat )
180+ }
181+ }
182+ importsModelModules (t , path .Join (outDir , "model.rs" ))
183+ }
184+ }
185+
186+ func TestRustNosvc (t * testing.T ) {
187+ requireProtoc (t )
188+ outDir := t .TempDir ()
189+
190+ cfg := & config.Config {
191+ General : config.GeneralConfig {
192+ SpecificationFormat : "protobuf" ,
193+ ServiceConfig : "google/cloud/secretmanager/v1/secretmanager_v1.yaml" ,
194+ SpecificationSource : "google/cloud/secretmanager/v1" ,
195+ },
196+ Source : map [string ]string {
197+ "googleapis-root" : path .Join (testdataDir , "googleapis" ),
198+ },
199+ Codec : map [string ]string {
200+ "copyright-year" : "2025" ,
201+ "template-override" : path .Join ("templates" , "nosvc" ),
202+ },
203+ }
204+ model , err := parser .CreateModel (cfg )
205+ if err != nil {
206+ t .Fatal (err )
207+ }
208+ if err := Generate (model , outDir , cfg ); err != nil {
209+ t .Fatal (err )
210+ }
211+ for _ , expected := range expectedInNosvc {
84212 filename := path .Join (outDir , expected )
85213 stat , err := os .Stat (filename )
86214 if os .IsNotExist (err ) {
@@ -90,6 +218,7 @@ func TestRustFromProtobuf(t *testing.T) {
90218 t .Errorf ("generated files should not be executable %s: %o" , filename , stat .Mode ())
91219 }
92220 }
221+ importsModelModules (t , path .Join (outDir , "src" , "model.rs" ))
93222}
94223
95224func TestRustModuleRpc (t * testing.T ) {
@@ -118,7 +247,7 @@ func TestRustModuleRpc(t *testing.T) {
118247 t .Fatal (err )
119248 }
120249
121- for _ , expected := range [] string { "mod.rs" } {
250+ for _ , expected := range expectedInModule {
122251 filename := path .Join (outDir , "rpc" , expected )
123252 stat , err := os .Stat (filename )
124253 if os .IsNotExist (err ) {
@@ -128,6 +257,7 @@ func TestRustModuleRpc(t *testing.T) {
128257 t .Errorf ("generated files should not be executable %s: %o" , filename , stat .Mode ())
129258 }
130259 }
260+ importsModelModules (t , path .Join (outDir , "rpc" , "mod.rs" ))
131261}
132262
133263func TestRustBootstrapWkt (t * testing.T ) {
@@ -157,7 +287,7 @@ func TestRustBootstrapWkt(t *testing.T) {
157287 t .Fatal (err )
158288 }
159289
160- for _ , expected := range [] string { "mod.rs" } {
290+ for _ , expected := range expectedInModule {
161291 filename := path .Join (outDir , "wkt" , expected )
162292 stat , err := os .Stat (filename )
163293 if os .IsNotExist (err ) {
@@ -169,6 +299,20 @@ func TestRustBootstrapWkt(t *testing.T) {
169299 }
170300}
171301
302+ func importsModelModules (t * testing.T , filename string ) {
303+ t .Helper ()
304+ contents , err := os .ReadFile (filename )
305+ if err != nil {
306+ t .Fatal (err )
307+ }
308+ lines := strings .Split (string (contents ), "\n " )
309+ for _ , want := range []string {"mod debug;" , "mod serialize;" , "mod deserialize;" } {
310+ if ! slices .Contains (lines , want ) {
311+ t .Errorf ("expected file %s to have a line matching %q, got:\n %s" , filename , want , contents )
312+ }
313+ }
314+ }
315+
172316func requireProtoc (t * testing.T ) {
173317 t .Helper ()
174318 if _ , err := exec .LookPath ("protoc" ); err != nil {
0 commit comments