|
| 1 | +package generator |
| 2 | + |
| 3 | +import ( |
| 4 | + "os" |
| 5 | + "path/filepath" |
| 6 | + "strings" |
| 7 | + "testing" |
| 8 | + |
| 9 | + "github.com/emicklei/proto" |
| 10 | + "github.com/stretchr/testify/assert" |
| 11 | + "github.com/stretchr/testify/require" |
| 12 | + conf "github.com/zeromicro/go-zero/tools/goctl/config" |
| 13 | + "github.com/zeromicro/go-zero/tools/goctl/rpc/parser" |
| 14 | + "github.com/zeromicro/go-zero/tools/goctl/util/stringx" |
| 15 | +) |
| 16 | + |
| 17 | +// mockDirContext is a minimal DirContext for unit-testing genCallGroup. |
| 18 | +type mockDirContext struct { |
| 19 | + callDir Dir |
| 20 | + pbDir Dir |
| 21 | + protoGo Dir |
| 22 | +} |
| 23 | + |
| 24 | +func (m *mockDirContext) GetCall() Dir { return m.callDir } |
| 25 | +func (m *mockDirContext) GetEtc() Dir { return Dir{} } |
| 26 | +func (m *mockDirContext) GetInternal() Dir { return Dir{} } |
| 27 | +func (m *mockDirContext) GetConfig() Dir { return Dir{} } |
| 28 | +func (m *mockDirContext) GetLogic() Dir { return Dir{} } |
| 29 | +func (m *mockDirContext) GetServer() Dir { return Dir{} } |
| 30 | +func (m *mockDirContext) GetSvc() Dir { return Dir{} } |
| 31 | +func (m *mockDirContext) GetPb() Dir { return m.pbDir } |
| 32 | +func (m *mockDirContext) GetProtoGo() Dir { return m.protoGo } |
| 33 | +func (m *mockDirContext) GetMain() Dir { return Dir{} } |
| 34 | +func (m *mockDirContext) GetServiceName() stringx.String { return stringx.From("test") } |
| 35 | +func (m *mockDirContext) SetPbDir(pbDir, grpcDir string) {} |
| 36 | + |
| 37 | +// TestGenCallGroup_OnlyUsedTypesAliased verifies that in multi-service mode each |
| 38 | +// generated client file contains type aliases only for the message types actually |
| 39 | +// used by that service's RPCs (fix for issue #5481). |
| 40 | +func TestGenCallGroup_OnlyUsedTypesAliased(t *testing.T) { |
| 41 | + tmpDir := t.TempDir() |
| 42 | + callBase := filepath.Join(tmpDir, "call") |
| 43 | + pbBase := filepath.Join(tmpDir, "pb") |
| 44 | + |
| 45 | + // Pre-create subdirs that genCallGroup will write into. |
| 46 | + require.NoError(t, os.MkdirAll(filepath.Join(callBase, "servicea"), 0755)) |
| 47 | + require.NoError(t, os.MkdirAll(filepath.Join(callBase, "serviceb"), 0755)) |
| 48 | + require.NoError(t, os.MkdirAll(pbBase, 0755)) |
| 49 | + |
| 50 | + mctx := &mockDirContext{ |
| 51 | + callDir: Dir{ |
| 52 | + Filename: callBase, |
| 53 | + Package: "example.com/multitest/call", |
| 54 | + Base: "call", |
| 55 | + GetChildPackage: func(childPath string) (string, error) { |
| 56 | + // Return a package path whose Base() is the lowercase service name. |
| 57 | + return filepath.Join(callBase, strings.ToLower(childPath)), nil |
| 58 | + }, |
| 59 | + }, |
| 60 | + pbDir: Dir{ |
| 61 | + Filename: pbBase, |
| 62 | + Package: "example.com/multitest/pb", |
| 63 | + Base: "pb", |
| 64 | + }, |
| 65 | + protoGo: Dir{ |
| 66 | + // Must differ from "servicea"/"serviceb" so isCallPkgSameToPbPkg stays false |
| 67 | + // and alias generation is triggered. |
| 68 | + Filename: pbBase, |
| 69 | + Package: "example.com/multitest/pb", |
| 70 | + Base: "pb", |
| 71 | + }, |
| 72 | + } |
| 73 | + |
| 74 | + // Proto with two services that use completely disjoint message types. |
| 75 | + protoData := parser.Proto{ |
| 76 | + Name: "multi.proto", |
| 77 | + PbPackage: "pb", |
| 78 | + Message: []parser.Message{ |
| 79 | + {Message: &proto.Message{Name: "AReq"}}, |
| 80 | + {Message: &proto.Message{Name: "AResp"}}, |
| 81 | + {Message: &proto.Message{Name: "BReq"}}, |
| 82 | + {Message: &proto.Message{Name: "BResp"}}, |
| 83 | + }, |
| 84 | + Service: parser.Services{ |
| 85 | + { |
| 86 | + Service: &proto.Service{Name: "ServiceA"}, |
| 87 | + RPC: []*parser.RPC{ |
| 88 | + {RPC: &proto.RPC{Name: "DoA", RequestType: "AReq", ReturnsType: "AResp"}}, |
| 89 | + }, |
| 90 | + }, |
| 91 | + { |
| 92 | + Service: &proto.Service{Name: "ServiceB"}, |
| 93 | + RPC: []*parser.RPC{ |
| 94 | + {RPC: &proto.RPC{Name: "DoB", RequestType: "BReq", ReturnsType: "BResp"}}, |
| 95 | + }, |
| 96 | + }, |
| 97 | + }, |
| 98 | + } |
| 99 | + |
| 100 | + cfg, err := conf.NewConfig("") |
| 101 | + require.NoError(t, err) |
| 102 | + |
| 103 | + g := NewGenerator("gozero", false) |
| 104 | + require.NoError(t, g.genCallGroup(mctx, protoData, cfg)) |
| 105 | + |
| 106 | + // servicea/servicea.go — aliases for AReq/AResp only |
| 107 | + aContent, err := os.ReadFile(filepath.Join(callBase, "servicea", "servicea.go")) |
| 108 | + require.NoError(t, err) |
| 109 | + aFile := string(aContent) |
| 110 | + |
| 111 | + assert.Contains(t, aFile, "AReq = pb.AReq", "ServiceA file should alias AReq") |
| 112 | + assert.Contains(t, aFile, "AResp = pb.AResp", "ServiceA file should alias AResp") |
| 113 | + assert.NotContains(t, aFile, "BReq = pb.BReq", "ServiceA file must not alias BReq") |
| 114 | + assert.NotContains(t, aFile, "BResp = pb.BResp", "ServiceA file must not alias BResp") |
| 115 | + |
| 116 | + // serviceb/serviceb.go — aliases for BReq/BResp only |
| 117 | + bContent, err := os.ReadFile(filepath.Join(callBase, "serviceb", "serviceb.go")) |
| 118 | + require.NoError(t, err) |
| 119 | + bFile := string(bContent) |
| 120 | + |
| 121 | + assert.Contains(t, bFile, "BReq = pb.BReq", "ServiceB file should alias BReq") |
| 122 | + assert.Contains(t, bFile, "BResp = pb.BResp", "ServiceB file should alias BResp") |
| 123 | + assert.NotContains(t, bFile, "AReq = pb.AReq", "ServiceB file must not alias AReq") |
| 124 | + assert.NotContains(t, bFile, "AResp = pb.AResp", "ServiceB file must not alias AResp") |
| 125 | +} |
0 commit comments