From b69c3433bbeec4811bb951474af1dfc867c0d641 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sat, 7 Jun 2025 09:24:15 +0900 Subject: [PATCH 01/27] test(protoc-gen-pothos): add snapshot tests for objectType printer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add comprehensive tests for createObjectTypeCode function - Test various protobuf message types (simple, nested, oneof, empty) - Support both ts-proto and protobuf-es libraries - Include tests for messages with GraphQL extensions - Generate snapshots to verify code output 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../__snapshots__/objectType.test.ts.snap | 801 ++++++++++++++++++ .../src/dslgen/printers/objectType.test.ts | 160 ++++ 2 files changed, 961 insertions(+) create mode 100644 packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/objectType.test.ts.snap create mode 100644 packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/objectType.test.ts.snap b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/objectType.test.ts.snap new file mode 100644 index 00000000..6688a50b --- /dev/null +++ b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/objectType.test.ts.snap @@ -0,0 +1,801 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createObjectTypeCode > protobuf-es > generates code for a message with nested fields 1`] = ` +"import { Message } from "@testapis/protobuf-es/testapis/primitives/primitives_pb"; +import { builder } from "../../../../builder"; + +export const Message$Ref = builder.objectRef("Message"); +builder.objectType(Message$Ref, { + "name": "Message", + "fields": (t) => ({ + requiredPrimitives: t.field({ + "type": Primitives$Ref, + "nullable": false, + "description": "Required.", + "resolve": (source) => { + return source.requiredPrimitives!; + }, + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitives: t.expose("optionalPrimitives", { + "type": Primitives$Ref, + "nullable": true, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + requiredPrimitivesList: t.field({ + "type": [Primitives$Ref], + "nullable": { "list": false, "items": false }, + "description": "Required.", + "resolve": (source) => { + return source.requiredPrimitivesList!; + }, + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitivesList: t.expose("optionalPrimitivesList", { + "type": [Primitives$Ref], + "nullable": { "list": true, "items": false }, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + }), + "isTypeOf": (source) => { + return source instanceof Message; + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > protobuf-es > generates code for a message with oneofs 1`] = ` +"import { OneofParent } from "@testapis/protobuf-es/testapis/oneof/oneof_pb"; +import { builder } from "../../../../builder"; + +export const OneofParent$Ref = builder.objectRef("OneofParent"); +builder.objectType(OneofParent$Ref, { + "name": "OneofParent", + "fields": (t) => ({ + normalField: t.expose("normalField", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }), + requiredOneofMembers: t.field({ + "type": OneofParentRequiredOneofMembers$Ref, + "nullable": false, + "description": "Required. disallow not_set.", + "resolve": (source) => { + const value = source.requiredOneofMembers.value; + if (value == null) { + throw new Error("requiredOneofMembers should not be null"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_oneof_members" } }, + }), + optionalOneofMembers: t.field({ + "type": OneofParentOptionalOneofMembers$Ref, + "nullable": true, + "resolve": (source) => { + return source.optionalOneofMembers.value; + }, + "extensions": { "protobufField": { "name": "optional_oneof_members" } }, + }), + }), + "isTypeOf": (source) => { + return source instanceof OneofParent; + }, + "extensions": { + "protobufMessage": { "fullName": "testapis.oneof.OneofParent", "name": "OneofParent", "package": "testapis.oneof" }, + }, +}); +" +`; + +exports[`createObjectTypeCode > protobuf-es > generates code for a simple message 1`] = ` +"import { Primitives } from "@testapis/protobuf-es/testapis/primitives/primitives_pb"; +import { builder } from "../../../../builder"; + +export const Primitives$Ref = builder.objectRef("Primitives"); +builder.objectType(Primitives$Ref, { + "name": "Primitives", + "fields": (t) => ({ + requiredDoubleValue: t.expose("requiredDoubleValue", { + "type": "Float", + "nullable": false, + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }), + requiredFloatValue: t.expose("requiredFloatValue", { + "type": "Float", + "nullable": false, + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }), + requiredInt32Value: t.expose("requiredInt32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }), + requiredInt64Value: t.expose("requiredInt64Value", { + "type": "Int64", + "nullable": false, + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }), + requiredUint32Value: t.expose("requiredUint32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }), + requiredUint64Value: t.expose("requiredUint64Value", { + "type": "Int64", + "nullable": false, + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }), + requiredSint32Value: t.expose("requiredSint32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }), + requiredSint64Value: t.expose("requiredSint64Value", { + "type": "Int64", + "nullable": false, + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }), + requiredFixed32Value: t.expose("requiredFixed32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }), + requiredFixed64Value: t.expose("requiredFixed64Value", { + "type": "Int64", + "nullable": false, + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }), + requiredSfixed32Value: t.expose("requiredSfixed32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }), + requiredSfixed64Value: t.expose("requiredSfixed64Value", { + "type": "Int64", + "nullable": false, + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }), + requiredBoolValue: t.expose("requiredBoolValue", { + "type": "Boolean", + "nullable": false, + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }), + requiredStringValue: t.expose("requiredStringValue", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }), + requiredBytesValue: t.field({ + "type": "Byte", + "nullable": false, + "resolve": (source) => { + return Buffer.from(source.requiredBytesValue); + }, + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }), + requiredDoubleValues: t.expose("requiredDoubleValues", { + "type": ["Float"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }), + requiredFloatValues: t.expose("requiredFloatValues", { + "type": ["Float"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }), + requiredInt32Values: t.expose("requiredInt32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }), + requiredInt64Values: t.expose("requiredInt64Values", { + "type": ["Int64"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }), + requiredUint32Values: t.expose("requiredUint32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }), + requiredUint64Values: t.expose("requiredUint64Values", { + "type": ["Int64"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }), + requiredSint32Values: t.expose("requiredSint32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }), + requiredSint64Values: t.expose("requiredSint64Values", { + "type": ["Int64"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }), + requiredFixed32Values: t.expose("requiredFixed32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }), + requiredFixed64Values: t.expose("requiredFixed64Values", { + "type": ["Int64"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }), + requiredSfixed32Values: t.expose("requiredSfixed32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }), + requiredSfixed64Values: t.expose("requiredSfixed64Values", { + "type": ["Int64"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }), + requiredBoolValues: t.expose("requiredBoolValues", { + "type": ["Boolean"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }), + requiredStringValues: t.expose("requiredStringValues", { + "type": ["String"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }), + requiredBytesValues: t.field({ + "type": ["Byte"], + "nullable": { "list": false, "items": false }, + "resolve": (source) => { + return source.requiredBytesValues.map((v) => Buffer.from(v)); + }, + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }), + }), + "isTypeOf": (source) => { + return source instanceof Primitives; + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Primitives", + "name": "Primitives", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > ts-proto > generates code for a message with nested fields 1`] = ` +"import { Message } from "@testapis/ts-proto/testapis/primitives/primitives"; +import { builder } from "../../../../builder"; + +export const Message$Ref = builder.objectRef("Message"); +builder.objectType(Message$Ref, { + "name": "Message", + "fields": (t) => ({ + requiredPrimitives: t.field({ + "type": Primitives$Ref, + "nullable": false, + "description": "Required.", + "resolve": (source) => { + return source.requiredPrimitives!; + }, + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitives: t.expose("optionalPrimitives", { + "type": Primitives$Ref, + "nullable": true, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + requiredPrimitivesList: t.field({ + "type": [Primitives$Ref], + "nullable": { "list": false, "items": false }, + "description": "Required.", + "resolve": (source) => { + return source.requiredPrimitivesList!; + }, + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitivesList: t.expose("optionalPrimitivesList", { + "type": [Primitives$Ref], + "nullable": { "list": true, "items": false }, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + }), + "isTypeOf": (source) => { + return (source as Message | { $type: string & {} }).$type === "testapis.primitives.Message"; + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > ts-proto > generates code for a message with oneofs 1`] = ` +"import { OneofParent } from "@testapis/ts-proto/testapis/oneof/oneof"; +import { builder } from "../../../../builder"; + +export const OneofParent$Ref = builder.objectRef("OneofParent"); +builder.objectType(OneofParent$Ref, { + "name": "OneofParent", + "fields": (t) => ({ + normalField: t.expose("normalField", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }), + requiredOneofMembers: t.field({ + "type": OneofParentRequiredOneofMembers$Ref, + "nullable": false, + "description": "Required. disallow not_set.", + "resolve": (source) => { + const value = source.requiredMessage1 ?? source.requiredMessage2; + if (value == null) { + throw new Error("requiredOneofMembers should not be null"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_oneof_members" } }, + }), + optionalOneofMembers: t.field({ + "type": OneofParentOptionalOneofMembers$Ref, + "nullable": true, + "resolve": (source) => { + const value = source.optoinalMessage1 ?? source.optoinalMessage2; + if (value == null) { + return null; + } + return value; + }, + "extensions": { "protobufField": { "name": "optional_oneof_members" } }, + }), + }), + "isTypeOf": (source) => { + return (source as OneofParent | { $type: string & {} }).$type === "testapis.oneof.OneofParent"; + }, + "extensions": { + "protobufMessage": { "fullName": "testapis.oneof.OneofParent", "name": "OneofParent", "package": "testapis.oneof" }, + }, +}); +" +`; + +exports[`createObjectTypeCode > ts-proto > generates code for a simple message 1`] = ` +"import { Primitives } from "@testapis/ts-proto/testapis/primitives/primitives"; +import { builder } from "../../../../builder"; + +export const Primitives$Ref = builder.objectRef("Primitives"); +builder.objectType(Primitives$Ref, { + "name": "Primitives", + "fields": (t) => ({ + requiredDoubleValue: t.expose("requiredDoubleValue", { + "type": "Float", + "nullable": false, + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }), + requiredFloatValue: t.expose("requiredFloatValue", { + "type": "Float", + "nullable": false, + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }), + requiredInt32Value: t.expose("requiredInt32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }), + requiredInt64Value: t.expose("requiredInt64Value", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }), + requiredUint32Value: t.expose("requiredUint32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }), + requiredUint64Value: t.expose("requiredUint64Value", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }), + requiredSint32Value: t.expose("requiredSint32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }), + requiredSint64Value: t.expose("requiredSint64Value", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }), + requiredFixed32Value: t.expose("requiredFixed32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }), + requiredFixed64Value: t.expose("requiredFixed64Value", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }), + requiredSfixed32Value: t.expose("requiredSfixed32Value", { + "type": "Int", + "nullable": false, + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }), + requiredSfixed64Value: t.expose("requiredSfixed64Value", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }), + requiredBoolValue: t.expose("requiredBoolValue", { + "type": "Boolean", + "nullable": false, + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }), + requiredStringValue: t.expose("requiredStringValue", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }), + requiredBytesValue: t.field({ + "type": "Byte", + "nullable": false, + "resolve": (source) => { + return Buffer.from(source.requiredBytesValue); + }, + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }), + requiredDoubleValues: t.expose("requiredDoubleValues", { + "type": ["Float"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }), + requiredFloatValues: t.expose("requiredFloatValues", { + "type": ["Float"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }), + requiredInt32Values: t.expose("requiredInt32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }), + requiredInt64Values: t.expose("requiredInt64Values", { + "type": ["String"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }), + requiredUint32Values: t.expose("requiredUint32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }), + requiredUint64Values: t.expose("requiredUint64Values", { + "type": ["String"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }), + requiredSint32Values: t.expose("requiredSint32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }), + requiredSint64Values: t.expose("requiredSint64Values", { + "type": ["String"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }), + requiredFixed32Values: t.expose("requiredFixed32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }), + requiredFixed64Values: t.expose("requiredFixed64Values", { + "type": ["String"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }), + requiredSfixed32Values: t.expose("requiredSfixed32Values", { + "type": ["Int"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }), + requiredSfixed64Values: t.expose("requiredSfixed64Values", { + "type": ["String"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }), + requiredBoolValues: t.expose("requiredBoolValues", { + "type": ["Boolean"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }), + requiredStringValues: t.expose("requiredStringValues", { + "type": ["String"], + "nullable": { "list": false, "items": false }, + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }), + requiredBytesValues: t.field({ + "type": ["Byte"], + "nullable": { "list": false, "items": false }, + "resolve": (source) => { + return source.requiredBytesValues.map((v) => Buffer.from(v)); + }, + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }), + }), + "isTypeOf": (source) => { + return (source as Primitives | { $type: string & {} }).$type === "testapis.primitives.Primitives"; + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Primitives", + "name": "Primitives", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > ts-proto > generates code for empty message 1`] = ` +"import { EmptyMessage } from "@testapis/ts-proto/testapis/empty_types/empty"; +import { builder } from "../../../../builder"; + +export const EmptyMessage$Ref = builder.objectRef("EmptyMessage"); +builder.objectType(EmptyMessage$Ref, { + "name": "EmptyMessage", + "fields": (t) => ({ + _: t.field({ type: "Boolean", nullable: true, description: "noop field", resolve: () => true }), + }), + "isTypeOf": (source) => { + return (source as EmptyMessage | { $type: string & {} }).$type === "testapis.empty_types.EmptyMessage"; + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.empty_types.EmptyMessage", + "name": "EmptyMessage", + "package": "testapis.empty_types", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > ts-proto > generates code for nested types 1`] = ` +"import { ParentMessage, ParentMessage_NestedEnum } from "@testapis/ts-proto/testapis/nested/nested"; +import { builder } from "../../../../builder"; + +export const ParentMessage$Ref = builder.objectRef("ParentMessage"); +builder.objectType(ParentMessage$Ref, { + "name": "ParentMessage", + "fields": (t) => ({ + body: t.expose("body", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "body", "typeFullName": "string" } }, + }), + nested: t.expose("nested", { + "type": ParentMessageNestedMessage$Ref, + "nullable": true, + "extensions": { + "protobufField": { "name": "nested", "typeFullName": "testapis.nested.ParentMessage.NestedMessage" }, + }, + }), + nestedEnum: t.field({ + "type": ParentMessageNestedEnum$Ref, + "nullable": true, + "resolve": (source) => { + if (source.nestedEnum === ParentMessage_NestedEnum.NESTED_ENUM_UNSPECIFIED) { + return null; + } + + return source.nestedEnum; + }, + "extensions": { + "protobufField": { "name": "nested_enum", "typeFullName": "testapis.nested.ParentMessage.NestedEnum" }, + }, + }), + }), + "isTypeOf": (source) => { + return (source as ParentMessage | { $type: string & {} }).$type === "testapis.nested.ParentMessage"; + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.nested.ParentMessage", + "name": "ParentMessage", + "package": "testapis.nested", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > with extensions > generates code for message with field extensions 1`] = ` +"import { EnumWillRename, PrefixedEnum, PrefixedMessage } from "@testapis/ts-proto/testapis/extensions/extensions"; +import { builder } from "../../../../builder"; + +export const TestPrefixPrefixedMessage$Ref = builder.objectRef("TestPrefixPrefixedMessage"); +builder.objectType(TestPrefixPrefixedMessage$Ref, { + "name": "TestPrefixPrefixedMessage", + "fields": (t) => ({ + id: t.expose("id", { + "type": "String", + "nullable": false, + "description": "Output only.", + "extensions": { "protobufField": { "name": "id", "typeFullName": "uint64" } }, + }), + body: t.expose("body", { + "type": "String", + "nullable": false, + "extensions": { "protobufField": { "name": "body", "typeFullName": "string" } }, + }), + prefixedEnum: t.field({ + "type": TestPrefixPrefixedEnum$Ref, + "nullable": true, + "resolve": (source) => { + if (source.prefixedEnum === PrefixedEnum.PREFIXED_ENUM_UNSPECIFIED) { + return null; + } + + if (source.prefixedEnum === PrefixedEnum.PREFIXED_IGNORED) { + throw new Error("PREFIXED_IGNORED is ignored in GraphQL schema"); + } + + return source.prefixedEnum; + }, + "extensions": { + "protobufField": { "name": "prefixed_enum", "typeFullName": "testapis.extensions.PrefixedEnum" }, + }, + }), + notIgnoredMessage: t.expose("notIgnoredMessage", { + "type": TestPrefixIgnoredMessageNotIgnored$Ref, + "nullable": true, + "extensions": { + "protobufField": { + "name": "not_ignored_message", + "typeFullName": "testapis.extensions.IgnoredMessage.NotIgnored", + }, + }, + }), + squashedMessage: t.field({ + "type": TestPrefixPrefixedMessageSquashedMessage$Ref, + "nullable": true, + "resolve": (source) => { + const value = source.squashedMessage?.oneofField ?? source.squashedMessage?.oneofField2; + if (value == null) { + return null; + } + return value; + }, + "extensions": { + "protobufField": { + "name": "squashed_message", + "typeFullName": "testapis.extensions.PrefixedMessage.SquashedMessage", + }, + }, + }), + thisFieldWasRenamed: t.expose("thisFieldWillBeRenamed", { + "type": "String", + "nullable": false, + "extensions": { + "protobufField": { + "name": "this_field_will_be_renamed", + "typeFullName": "string", + "options": { "[graphql.field]": { "name": "thisFieldWasRenamed" } }, + }, + }, + }), + skipResolver: t.expose("skipResolver", { + "type": "String", + "nullable": false, + "extensions": { + "protobufField": { + "name": "skip_resolver", + "typeFullName": "string", + "options": { "[graphql.field]": { "skipResolver": true } }, + }, + }, + }), + squashedMessages: t.field({ + "type": [TestPrefixPrefixedMessageSquashedMessage$Ref], + "nullable": { "list": true, "items": false }, + "resolve": (source) => { + return source.squashedMessages.map((item) => { + const value = item?.oneofField ?? item?.oneofField2; + if (value == null) { + throw new Error("squashedMessages should not be null"); + } + return value; + }); + }, + "extensions": { + "protobufField": { + "name": "squashed_messages", + "typeFullName": "testapis.extensions.PrefixedMessage.SquashedMessage", + }, + }, + }), + renamedMessage: t.expose("renamedMessage", { + "type": TestPrefixRenamedMessage$Ref, + "nullable": true, + "extensions": { + "protobufField": { "name": "renamed_message", "typeFullName": "testapis.extensions.MessageWillRename" }, + }, + }), + renamedEnum: t.field({ + "type": TestPrefixRenamedEnum$Ref, + "nullable": true, + "resolve": (source) => { + if (source.renamedEnum === EnumWillRename.ENUM_WILL_RENAME_UNSPECIFIED) { + return null; + } + + return source.renamedEnum; + }, + "extensions": { + "protobufField": { "name": "renamed_enum", "typeFullName": "testapis.extensions.EnumWillRename" }, + }, + }), + partialIgnoreOneof: t.field({ + "type": TestPrefixPrefixedMessagePartialIgnoreOneof$Ref, + "nullable": true, + "resolve": (source) => { + const value = source.oneofNotIgnoredField; + if (value == null) { + return null; + } + return value; + }, + "extensions": { "protobufField": { "name": "partial_ignore_oneof" } }, + }), + }), + "isTypeOf": (source) => { + return (source as PrefixedMessage | { $type: string & {} }).$type === "testapis.extensions.PrefixedMessage"; + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.extensions.PrefixedMessage", + "name": "PrefixedMessage", + "package": "testapis.extensions", + }, + }, +}); +" +`; diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts new file mode 100644 index 00000000..0a9250c3 --- /dev/null +++ b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts @@ -0,0 +1,160 @@ +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; +import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { + ObjectType, + collectTypesFromFile, + createRegistryFromSchema, + defaultScalarMappingForTsProto, + defaultScalarMapping +} from "@proto-graphql/codegen-core"; +import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { code } from "ts-poet"; +import { describe, expect, test } from "vitest"; +import { createObjectTypeCode } from "./objectType.js"; +import type { PothosPrinterOptions } from "./util.js"; + +// Helper to extract ObjectType instances and generate code +function generateObjectTypeCode(packageName: string, messageTypeName: string, options: PothosPrinterOptions) { + const req = buildCodeGeneratorRequest(packageName); + + // Default type options + const typeOptions = { + partialInputs: false, + scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + // Create a minimal plugin to get Schema + const plugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: createTsGenerator({ + generateFiles: (schema, file) => { + // Just return, we only need the schema + }, + dsl: "pothos", + }), + parseOptions: parsePothosOptions, + }); + + // Run the plugin to get the transformed schema + const resp = plugin.run(req); + + // Now we need to recreate the schema to access its internals + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + // Find the target file + const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); + if (!targetFile) throw new Error(`File for ${packageName} not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const objectType = types.find(t => t.typeName === messageTypeName && t instanceof ObjectType) as ObjectType; + if (!objectType) throw new Error(`${messageTypeName} type not found`); + + const generatedCode = createObjectTypeCode(objectType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parsePothosOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + return result; +} + +describe("createObjectTypeCode", () => { + describe("ts-proto", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "ts-proto" as const, + importPrefix: "@testapis/ts-proto", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for a simple message", () => { + const code = generateObjectTypeCode("testapis.primitives", "Primitives", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a message with nested fields", () => { + const code = generateObjectTypeCode("testapis.primitives", "Message", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a message with oneofs", () => { + const code = generateObjectTypeCode("testapis.oneof", "OneofParent", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for empty message", () => { + const code = generateObjectTypeCode("testapis.empty_types", "EmptyMessage", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for nested types", () => { + const code = generateObjectTypeCode("testapis.nested", "ParentMessage", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("protobuf-es", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "protobuf-es" as const, + importPrefix: "@testapis/protobuf-es", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for a simple message", () => { + const code = generateObjectTypeCode("testapis.primitives", "Primitives", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a message with nested fields", () => { + const code = generateObjectTypeCode("testapis.primitives", "Message", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a message with oneofs", () => { + const code = generateObjectTypeCode("testapis.oneof", "OneofParent", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("with extensions", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "ts-proto" as const, + importPrefix: "@testapis/ts-proto", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for message with field extensions", () => { + const code = generateObjectTypeCode("testapis.extensions", "TestPrefixPrefixedMessage", options); + expect(code).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file From 0cb9f585387315ca11fddf6bc2f9f2bb89df1edd Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sat, 7 Jun 2025 09:28:28 +0900 Subject: [PATCH 02/27] test(protoc-gen-pothos): add snapshot tests for enumType printer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tests for createEnumTypeCode function - Test simple enums, enums without unspecified value - Test nested enums and enums with extensions - Support both ts-proto and protobuf-es libraries 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../__snapshots__/enumType.test.ts.snap | 159 ++++++++++++++++++ .../src/dslgen/printers/enumType.test.ts | 136 +++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/enumType.test.ts.snap create mode 100644 packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/enumType.test.ts.snap b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/enumType.test.ts.snap new file mode 100644 index 00000000..2f94cffe --- /dev/null +++ b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/enumType.test.ts.snap @@ -0,0 +1,159 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createEnumTypeCode > protobuf-es > generates code for a simple enum 1`] = ` +"import { EnumRef } from "@pothos/core"; +import { MyEnum } from "@testapis/protobuf-es/testapis/enums/enums_pb"; +import { builder } from "../../../../builder"; + +export const MyEnum$Ref: EnumRef = builder.enumType("MyEnum", { + "values": { + FOO: { "value": 1, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_FOO" } } }, + BAR: { + "description": "This is Bar.", + "value": 2, + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_BAR" } }, + }, + BAZ: { "value": 3, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_BAZ" } } }, + } as const, + "extensions": { + "protobufEnum": { "name": "MyEnum", "fullName": "testapi.enums.MyEnum", "package": "testapi.enums" }, + }, +}); +" +`; + +exports[`createEnumTypeCode > protobuf-es > generates code for an enum without unspecified 1`] = ` +"import { EnumRef } from "@pothos/core"; +import { MyEnumWithoutUnspecified } from "@testapis/protobuf-es/testapis/enums/enums_pb"; +import { builder } from "../../../../builder"; + +export const MyEnumWithoutUnspecified$Ref: EnumRef = builder + .enumType("MyEnumWithoutUnspecified", { + "values": { + FOO: { "value": 0, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_FOO" } } }, + BAR: { "value": 1, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_BAR" } } }, + BAZ: { "value": 2, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_BAZ" } } }, + } as const, + "extensions": { + "protobufEnum": { + "name": "MyEnumWithoutUnspecified", + "fullName": "testapi.enums.MyEnumWithoutUnspecified", + "package": "testapi.enums", + }, + }, + }); +" +`; + +exports[`createEnumTypeCode > protobuf-es > generates code for nested enum 1`] = ` +"import { EnumRef } from "@pothos/core"; +import { ParentMessage_NestedEnum } from "@testapis/protobuf-es/testapis/nested/nested_pb"; +import { builder } from "../../../../builder"; + +export const ParentMessageNestedEnum$Ref: EnumRef = builder + .enumType("ParentMessageNestedEnum", { + "values": { + FOO: { "value": 1, "extensions": { "protobufEnumValue": { "name": "FOO" } } }, + BAR: { "value": 2, "extensions": { "protobufEnumValue": { "name": "BAR" } } }, + } as const, + "extensions": { + "protobufEnum": { + "name": "NestedEnum", + "fullName": "testapis.nested.ParentMessage.NestedEnum", + "package": "testapis.nested", + }, + }, + }); +" +`; + +exports[`createEnumTypeCode > ts-proto > generates code for a simple enum 1`] = ` +"import { EnumRef } from "@pothos/core"; +import { MyEnum } from "@testapis/ts-proto/testapis/enums/enums"; +import { builder } from "../../../../builder"; + +export const MyEnum$Ref: EnumRef = builder.enumType("MyEnum", { + "values": { + FOO: { "value": 1, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_FOO" } } }, + BAR: { + "description": "This is Bar.", + "value": 2, + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_BAR" } }, + }, + BAZ: { "value": 3, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_BAZ" } } }, + } as const, + "extensions": { + "protobufEnum": { "name": "MyEnum", "fullName": "testapi.enums.MyEnum", "package": "testapi.enums" }, + }, +}); +" +`; + +exports[`createEnumTypeCode > ts-proto > generates code for an enum without unspecified 1`] = ` +"import { EnumRef } from "@pothos/core"; +import { MyEnumWithoutUnspecified } from "@testapis/ts-proto/testapis/enums/enums"; +import { builder } from "../../../../builder"; + +export const MyEnumWithoutUnspecified$Ref: EnumRef = builder + .enumType("MyEnumWithoutUnspecified", { + "values": { + FOO: { "value": 0, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_FOO" } } }, + BAR: { "value": 1, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_BAR" } } }, + BAZ: { "value": 2, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_BAZ" } } }, + } as const, + "extensions": { + "protobufEnum": { + "name": "MyEnumWithoutUnspecified", + "fullName": "testapi.enums.MyEnumWithoutUnspecified", + "package": "testapi.enums", + }, + }, + }); +" +`; + +exports[`createEnumTypeCode > ts-proto > generates code for enum with extensions 1`] = ` +"import { EnumRef } from "@pothos/core"; +import { PrefixedEnum } from "@testapis/ts-proto/testapis/extensions/extensions"; +import { builder } from "../../../../builder"; + +export const TestPrefixPrefixedEnum$Ref: EnumRef = builder.enumType( + "TestPrefixPrefixedEnum", + { + "values": { + PREFIXED_FOO: { "value": 1, "extensions": { "protobufEnumValue": { "name": "PREFIXED_FOO" } } }, + PREFIXED_BAR: { "value": 2, "extensions": { "protobufEnumValue": { "name": "PREFIXED_BAR" } } }, + } as const, + "extensions": { + "protobufEnum": { + "name": "PrefixedEnum", + "fullName": "testapis.extensions.PrefixedEnum", + "package": "testapis.extensions", + }, + }, + }, +); +" +`; + +exports[`createEnumTypeCode > ts-proto > generates code for nested enum 1`] = ` +"import { EnumRef } from "@pothos/core"; +import { ParentMessage_NestedEnum } from "@testapis/ts-proto/testapis/nested/nested"; +import { builder } from "../../../../builder"; + +export const ParentMessageNestedEnum$Ref: EnumRef = builder + .enumType("ParentMessageNestedEnum", { + "values": { + FOO: { "value": 1, "extensions": { "protobufEnumValue": { "name": "FOO" } } }, + BAR: { "value": 2, "extensions": { "protobufEnumValue": { "name": "BAR" } } }, + } as const, + "extensions": { + "protobufEnum": { + "name": "NestedEnum", + "fullName": "testapis.nested.ParentMessage.NestedEnum", + "package": "testapis.nested", + }, + }, + }); +" +`; diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts new file mode 100644 index 00000000..877a907d --- /dev/null +++ b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts @@ -0,0 +1,136 @@ +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; +import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { + EnumType, + collectTypesFromFile, + createRegistryFromSchema, + defaultScalarMappingForTsProto, + defaultScalarMapping +} from "@proto-graphql/codegen-core"; +import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { code } from "ts-poet"; +import { describe, expect, test } from "vitest"; +import { createEnumTypeCode } from "./enumType.js"; +import type { PothosPrinterOptions } from "./util.js"; + +// Helper to extract EnumType instances and generate code +function generateEnumTypeCode(packageName: string, enumTypeName: string, options: PothosPrinterOptions) { + const req = buildCodeGeneratorRequest(packageName); + + // Default type options + const typeOptions = { + partialInputs: false, + scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + // Create a minimal plugin to get Schema + const plugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: createTsGenerator({ + generateFiles: (schema, file) => { + // Just return, we only need the schema + }, + dsl: "pothos", + }), + parseOptions: parsePothosOptions, + }); + + // Run the plugin to get the transformed schema + const resp = plugin.run(req); + + // Now we need to recreate the schema to access its internals + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + // Find the target file + const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); + if (!targetFile) throw new Error(`File for ${packageName} not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const enumType = types.find(t => t.typeName === enumTypeName && t instanceof EnumType) as EnumType; + if (!enumType) throw new Error(`${enumTypeName} type not found`); + + const generatedCode = createEnumTypeCode(enumType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parsePothosOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + return result; +} + +describe("createEnumTypeCode", () => { + describe("ts-proto", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "ts-proto" as const, + importPrefix: "@testapis/ts-proto", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for a simple enum", () => { + const code = generateEnumTypeCode("testapis.enums", "MyEnum", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an enum without unspecified", () => { + const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for nested enum", () => { + const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for enum with extensions", () => { + const code = generateEnumTypeCode("testapis.extensions", "TestPrefixPrefixedEnum", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("protobuf-es", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "protobuf-es" as const, + importPrefix: "@testapis/protobuf-es", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for a simple enum", () => { + const code = generateEnumTypeCode("testapis.enums", "MyEnum", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an enum without unspecified", () => { + const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for nested enum", () => { + const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum", options); + expect(code).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file From 1fa1f7653acb0683b91fa4d64ed9e0cbdfc3a8dd Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sat, 7 Jun 2025 09:30:30 +0900 Subject: [PATCH 03/27] test(protoc-gen-pothos): add snapshot tests for inputObjectType printer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tests for createInputObjectTypeCode function - Test simple inputs, nested fields, oneof fields, empty inputs - Test partial input types generation - Support both ts-proto and protobuf-es libraries - Include toProto function generation for protobuf-es 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../inputObjectType.test.ts.snap | 854 ++++++++++++++++++ .../dslgen/printers/inputObjectType.test.ts | 199 ++++ 2 files changed, 1053 insertions(+) create mode 100644 packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap create mode 100644 packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap new file mode 100644 index 00000000..27b955fa --- /dev/null +++ b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap @@ -0,0 +1,854 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createInputObjectTypeCode > protobuf-es > generates code for a simple input object 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { Primitives } from "@testapis/protobuf-es/testapis/primitives/primitives_pb"; +import { builder } from "../../../../builder"; + +export type PrimitivesInput$Shape = { + requiredDoubleValue: Primitives["requiredDoubleValue"]; + requiredFloatValue: Primitives["requiredFloatValue"]; + requiredInt32Value: Primitives["requiredInt32Value"]; + requiredInt64Value: Primitives["requiredInt64Value"]; + requiredUint32Value: Primitives["requiredUint32Value"]; + requiredUint64Value: Primitives["requiredUint64Value"]; + requiredSint32Value: Primitives["requiredSint32Value"]; + requiredSint64Value: Primitives["requiredSint64Value"]; + requiredFixed32Value: Primitives["requiredFixed32Value"]; + requiredFixed64Value: Primitives["requiredFixed64Value"]; + requiredSfixed32Value: Primitives["requiredSfixed32Value"]; + requiredSfixed64Value: Primitives["requiredSfixed64Value"]; + requiredBoolValue: Primitives["requiredBoolValue"]; + requiredStringValue: Primitives["requiredStringValue"]; + requiredBytesValue: Primitives["requiredBytesValue"]; + requiredDoubleValues: Primitives["requiredDoubleValues"]; + requiredFloatValues: Primitives["requiredFloatValues"]; + requiredInt32Values: Primitives["requiredInt32Values"]; + requiredInt64Values: Primitives["requiredInt64Values"]; + requiredUint32Values: Primitives["requiredUint32Values"]; + requiredUint64Values: Primitives["requiredUint64Values"]; + requiredSint32Values: Primitives["requiredSint32Values"]; + requiredSint64Values: Primitives["requiredSint64Values"]; + requiredFixed32Values: Primitives["requiredFixed32Values"]; + requiredFixed64Values: Primitives["requiredFixed64Values"]; + requiredSfixed32Values: Primitives["requiredSfixed32Values"]; + requiredSfixed64Values: Primitives["requiredSfixed64Values"]; + requiredBoolValues: Primitives["requiredBoolValues"]; + requiredStringValues: Primitives["requiredStringValues"]; + requiredBytesValues: Primitives["requiredBytesValues"]; +}; + +export const PrimitivesInput$Ref: InputObjectRef = builder.inputRef( + "PrimitivesInput", +).implement({ + "fields": (t) => ({ + requiredDoubleValue: t.field({ + "type": "Float", + "required": true, + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }), + requiredFloatValue: t.field({ + "type": "Float", + "required": true, + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }), + requiredInt32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }), + requiredInt64Value: t.field({ + "type": "Int64", + "required": true, + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }), + requiredUint32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }), + requiredUint64Value: t.field({ + "type": "Int64", + "required": true, + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }), + requiredSint32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }), + requiredSint64Value: t.field({ + "type": "Int64", + "required": true, + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }), + requiredFixed32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }), + requiredFixed64Value: t.field({ + "type": "Int64", + "required": true, + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }), + requiredSfixed32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }), + requiredSfixed64Value: t.field({ + "type": "Int64", + "required": true, + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }), + requiredBoolValue: t.field({ + "type": "Boolean", + "required": true, + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }), + requiredStringValue: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }), + requiredBytesValue: t.field({ + "type": "Byte", + "required": true, + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }), + requiredDoubleValues: t.field({ + "type": ["Float"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }), + requiredFloatValues: t.field({ + "type": ["Float"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }), + requiredInt32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }), + requiredInt64Values: t.field({ + "type": ["Int64"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }), + requiredUint32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }), + requiredUint64Values: t.field({ + "type": ["Int64"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }), + requiredSint32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }), + requiredSint64Values: t.field({ + "type": ["Int64"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }), + requiredFixed32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }), + requiredFixed64Values: t.field({ + "type": ["Int64"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }), + requiredSfixed32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }), + requiredSfixed64Values: t.field({ + "type": ["Int64"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }), + requiredBoolValues: t.field({ + "type": ["Boolean"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }), + requiredStringValues: t.field({ + "type": ["String"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }), + requiredBytesValues: t.field({ + "type": ["Byte"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }), + }), + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Primitives", + "name": "Primitives", + "package": "testapis.primitives", + }, + }, +}); + +export function PrimitivesInput$toProto(input: PrimitivesInput$Shape | null | undefined): Primitives { + return new Primitives({ + requiredDoubleValue: input?.requiredDoubleValue ?? undefined, + requiredFloatValue: input?.requiredFloatValue ?? undefined, + requiredInt32Value: input?.requiredInt32Value ?? undefined, + requiredInt64Value: input?.requiredInt64Value ?? undefined, + requiredUint32Value: input?.requiredUint32Value ?? undefined, + requiredUint64Value: input?.requiredUint64Value ?? undefined, + requiredSint32Value: input?.requiredSint32Value ?? undefined, + requiredSint64Value: input?.requiredSint64Value ?? undefined, + requiredFixed32Value: input?.requiredFixed32Value ?? undefined, + requiredFixed64Value: input?.requiredFixed64Value ?? undefined, + requiredSfixed32Value: input?.requiredSfixed32Value ?? undefined, + requiredSfixed64Value: input?.requiredSfixed64Value ?? undefined, + requiredBoolValue: input?.requiredBoolValue ?? undefined, + requiredStringValue: input?.requiredStringValue ?? undefined, + requiredBytesValue: input?.requiredBytesValue ?? undefined, + requiredDoubleValues: input?.requiredDoubleValues ?? undefined, + requiredFloatValues: input?.requiredFloatValues ?? undefined, + requiredInt32Values: input?.requiredInt32Values ?? undefined, + requiredInt64Values: input?.requiredInt64Values ?? undefined, + requiredUint32Values: input?.requiredUint32Values ?? undefined, + requiredUint64Values: input?.requiredUint64Values ?? undefined, + requiredSint32Values: input?.requiredSint32Values ?? undefined, + requiredSint64Values: input?.requiredSint64Values ?? undefined, + requiredFixed32Values: input?.requiredFixed32Values ?? undefined, + requiredFixed64Values: input?.requiredFixed64Values ?? undefined, + requiredSfixed32Values: input?.requiredSfixed32Values ?? undefined, + requiredSfixed64Values: input?.requiredSfixed64Values ?? undefined, + requiredBoolValues: input?.requiredBoolValues ?? undefined, + requiredStringValues: input?.requiredStringValues ?? undefined, + requiredBytesValues: input?.requiredBytesValues ?? undefined, + }); +} +" +`; + +exports[`createInputObjectTypeCode > protobuf-es > generates code for an input object with nested fields 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { Message } from "@testapis/protobuf-es/testapis/primitives/primitives_pb"; +import { builder } from "../../../../builder"; + +export type MessageInput$Shape = { + requiredPrimitives: PrimitivesInput$Shape; + optionalPrimitives?: PrimitivesInput$Shape | null; + requiredPrimitivesList: Array; + optionalPrimitivesList?: Array | null; +}; + +export const MessageInput$Ref: InputObjectRef = builder.inputRef("MessageInput") + .implement({ + "fields": (t) => ({ + requiredPrimitives: t.field({ + "type": PrimitivesInput$Ref, + "required": true, + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitives: t.field({ + "type": PrimitivesInput$Ref, + "required": false, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + requiredPrimitivesList: t.field({ + "type": [PrimitivesInput$Ref], + "required": { "list": true, "items": true }, + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitivesList: t.field({ + "type": [PrimitivesInput$Ref], + "required": { "list": false, "items": true }, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + }), + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, + }); + +export function MessageInput$toProto(input: MessageInput$Shape | null | undefined): Message { + return new Message({ + requiredPrimitives: input?.requiredPrimitives ? PrimitivesInput$toProto(input.requiredPrimitives) : undefined, + optionalPrimitives: input?.optionalPrimitives ? PrimitivesInput$toProto(input.optionalPrimitives) : undefined, + requiredPrimitivesList: input?.requiredPrimitivesList?.map((v) => PrimitivesInput$toProto(v)), + optionalPrimitivesList: input?.optionalPrimitivesList?.map((v) => PrimitivesInput$toProto(v)), + }); +} +" +`; + +exports[`createInputObjectTypeCode > protobuf-es > generates code for an input object with oneof fields 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { OneofParent } from "@testapis/protobuf-es/testapis/oneof/oneof_pb"; +import { builder } from "../../../../builder"; + +export type OneofParentInput$Shape = { + normalField: OneofParent["normalField"]; + requiredMessage1?: OneofMemberMessage1Input$Shape | null; + requiredMessage2?: OneofMemberMessage2Input$Shape | null; + optoinalMessage1?: OneofMemberMessage1Input$Shape | null; + optoinalMessage2?: OneofMemberMessage2Input$Shape | null; +}; + +export const OneofParentInput$Ref: InputObjectRef = builder.inputRef( + "OneofParentInput", +).implement({ + "fields": (t) => ({ + normalField: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }), + requiredMessage1: t.field({ + "type": OneofMemberMessage1Input$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "required_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }), + requiredMessage2: t.field({ + "type": OneofMemberMessage2Input$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "required_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }), + optoinalMessage1: t.field({ + "type": OneofMemberMessage1Input$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "optoinal_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }), + optoinalMessage2: t.field({ + "type": OneofMemberMessage2Input$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "optoinal_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }), + }), + "extensions": { + "protobufMessage": { "fullName": "testapis.oneof.OneofParent", "name": "OneofParent", "package": "testapis.oneof" }, + }, +}); + +export function OneofParentInput$toProto(input: OneofParentInput$Shape | null | undefined): OneofParent { + return new OneofParent({ + normalField: input?.normalField ?? undefined, + requiredOneofMembers: input?.requiredMessage1 + ? { case: "requiredMessage1", value: OneofMemberMessage1Input$toProto(input.requiredMessage1) } + : input?.requiredMessage2 + ? { case: "requiredMessage2", value: OneofMemberMessage2Input$toProto(input.requiredMessage2) } + : undefined, + optionalOneofMembers: input?.optoinalMessage1 + ? { case: "optoinalMessage1", value: OneofMemberMessage1Input$toProto(input.optoinalMessage1) } + : input?.optoinalMessage2 + ? { case: "optoinalMessage2", value: OneofMemberMessage2Input$toProto(input.optoinalMessage2) } + : undefined, + }); +} +" +`; + +exports[`createInputObjectTypeCode > protobuf-es > generates code for empty input object 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { EmptyMessage } from "@testapis/protobuf-es/testapis/empty_types/empty_pb"; +import { builder } from "../../../../builder"; + +export type EmptyMessageInput$Shape = {}; + +export const EmptyMessageInput$Ref: InputObjectRef = builder.inputRef( + "EmptyMessageInput", +).implement({ + "fields": (t) => ({ _: t.field({ type: "Boolean", required: false, description: "noop field" }) }), + "extensions": { + "protobufMessage": { + "fullName": "testapis.empty_types.EmptyMessage", + "name": "EmptyMessage", + "package": "testapis.empty_types", + }, + }, +}); + +export function EmptyMessageInput$toProto(input: EmptyMessageInput$Shape | null | undefined): EmptyMessage { + return new EmptyMessage({}); +} +" +`; + +exports[`createInputObjectTypeCode > ts-proto > generates code for a simple input object 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { Primitives } from "@testapis/ts-proto/testapis/primitives/primitives"; +import { builder } from "../../../../builder"; + +export type PrimitivesInput$Shape = { + requiredDoubleValue: Primitives["requiredDoubleValue"]; + requiredFloatValue: Primitives["requiredFloatValue"]; + requiredInt32Value: Primitives["requiredInt32Value"]; + requiredInt64Value: Primitives["requiredInt64Value"]; + requiredUint32Value: Primitives["requiredUint32Value"]; + requiredUint64Value: Primitives["requiredUint64Value"]; + requiredSint32Value: Primitives["requiredSint32Value"]; + requiredSint64Value: Primitives["requiredSint64Value"]; + requiredFixed32Value: Primitives["requiredFixed32Value"]; + requiredFixed64Value: Primitives["requiredFixed64Value"]; + requiredSfixed32Value: Primitives["requiredSfixed32Value"]; + requiredSfixed64Value: Primitives["requiredSfixed64Value"]; + requiredBoolValue: Primitives["requiredBoolValue"]; + requiredStringValue: Primitives["requiredStringValue"]; + requiredBytesValue: Primitives["requiredBytesValue"]; + requiredDoubleValues: Primitives["requiredDoubleValues"]; + requiredFloatValues: Primitives["requiredFloatValues"]; + requiredInt32Values: Primitives["requiredInt32Values"]; + requiredInt64Values: Primitives["requiredInt64Values"]; + requiredUint32Values: Primitives["requiredUint32Values"]; + requiredUint64Values: Primitives["requiredUint64Values"]; + requiredSint32Values: Primitives["requiredSint32Values"]; + requiredSint64Values: Primitives["requiredSint64Values"]; + requiredFixed32Values: Primitives["requiredFixed32Values"]; + requiredFixed64Values: Primitives["requiredFixed64Values"]; + requiredSfixed32Values: Primitives["requiredSfixed32Values"]; + requiredSfixed64Values: Primitives["requiredSfixed64Values"]; + requiredBoolValues: Primitives["requiredBoolValues"]; + requiredStringValues: Primitives["requiredStringValues"]; + requiredBytesValues: Primitives["requiredBytesValues"]; +}; + +export const PrimitivesInput$Ref: InputObjectRef = builder.inputRef( + "PrimitivesInput", +).implement({ + "fields": (t) => ({ + requiredDoubleValue: t.field({ + "type": "Float", + "required": true, + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }), + requiredFloatValue: t.field({ + "type": "Float", + "required": true, + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }), + requiredInt32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }), + requiredInt64Value: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }), + requiredUint32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }), + requiredUint64Value: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }), + requiredSint32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }), + requiredSint64Value: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }), + requiredFixed32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }), + requiredFixed64Value: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }), + requiredSfixed32Value: t.field({ + "type": "Int", + "required": true, + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }), + requiredSfixed64Value: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }), + requiredBoolValue: t.field({ + "type": "Boolean", + "required": true, + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }), + requiredStringValue: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }), + requiredBytesValue: t.field({ + "type": "Byte", + "required": true, + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }), + requiredDoubleValues: t.field({ + "type": ["Float"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }), + requiredFloatValues: t.field({ + "type": ["Float"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }), + requiredInt32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }), + requiredInt64Values: t.field({ + "type": ["String"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }), + requiredUint32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }), + requiredUint64Values: t.field({ + "type": ["String"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }), + requiredSint32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }), + requiredSint64Values: t.field({ + "type": ["String"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }), + requiredFixed32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }), + requiredFixed64Values: t.field({ + "type": ["String"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }), + requiredSfixed32Values: t.field({ + "type": ["Int"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }), + requiredSfixed64Values: t.field({ + "type": ["String"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }), + requiredBoolValues: t.field({ + "type": ["Boolean"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }), + requiredStringValues: t.field({ + "type": ["String"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }), + requiredBytesValues: t.field({ + "type": ["Byte"], + "required": { "list": true, "items": true }, + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }), + }), + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Primitives", + "name": "Primitives", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createInputObjectTypeCode > ts-proto > generates code for an input object with nested fields 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { builder } from "../../../../builder"; + +export type MessageInput$Shape = { + requiredPrimitives: PrimitivesInput$Shape; + optionalPrimitives?: PrimitivesInput$Shape | null; + requiredPrimitivesList: Array; + optionalPrimitivesList?: Array | null; +}; + +export const MessageInput$Ref: InputObjectRef = builder.inputRef("MessageInput") + .implement({ + "fields": (t) => ({ + requiredPrimitives: t.field({ + "type": PrimitivesInput$Ref, + "required": true, + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitives: t.field({ + "type": PrimitivesInput$Ref, + "required": false, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + requiredPrimitivesList: t.field({ + "type": [PrimitivesInput$Ref], + "required": { "list": true, "items": true }, + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitivesList: t.field({ + "type": [PrimitivesInput$Ref], + "required": { "list": false, "items": true }, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + }), + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, + }); +" +`; + +exports[`createInputObjectTypeCode > ts-proto > generates code for an input object with oneof fields 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { OneofParent } from "@testapis/ts-proto/testapis/oneof/oneof"; +import { builder } from "../../../../builder"; + +export type OneofParentInput$Shape = { + normalField: OneofParent["normalField"]; + requiredMessage1?: OneofMemberMessage1Input$Shape | null; + requiredMessage2?: OneofMemberMessage2Input$Shape | null; + optoinalMessage1?: OneofMemberMessage1Input$Shape | null; + optoinalMessage2?: OneofMemberMessage2Input$Shape | null; +}; + +export const OneofParentInput$Ref: InputObjectRef = builder.inputRef( + "OneofParentInput", +).implement({ + "fields": (t) => ({ + normalField: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }), + requiredMessage1: t.field({ + "type": OneofMemberMessage1Input$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "required_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }), + requiredMessage2: t.field({ + "type": OneofMemberMessage2Input$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "required_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }), + optoinalMessage1: t.field({ + "type": OneofMemberMessage1Input$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "optoinal_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }), + optoinalMessage2: t.field({ + "type": OneofMemberMessage2Input$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "optoinal_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }), + }), + "extensions": { + "protobufMessage": { "fullName": "testapis.oneof.OneofParent", "name": "OneofParent", "package": "testapis.oneof" }, + }, +}); +" +`; + +exports[`createInputObjectTypeCode > ts-proto > generates code for empty input object 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { builder } from "../../../../builder"; + +export type EmptyMessageInput$Shape = {}; + +export const EmptyMessageInput$Ref: InputObjectRef = builder.inputRef( + "EmptyMessageInput", +).implement({ + "fields": (t) => ({ _: t.field({ type: "Boolean", required: false, description: "noop field" }) }), + "extensions": { + "protobufMessage": { + "fullName": "testapis.empty_types.EmptyMessage", + "name": "EmptyMessage", + "package": "testapis.empty_types", + }, + }, +}); +" +`; + +exports[`createInputObjectTypeCode > ts-proto > generates code for nested input types 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { ParentMessage } from "@testapis/ts-proto/testapis/nested/nested"; +import { builder } from "../../../../builder"; + +export type ParentMessageInput$Shape = { + body: ParentMessage["body"]; + nested?: ParentMessageNestedMessageInput$Shape | null; + nestedEnum?: ParentMessage["nestedEnum"] | null; +}; + +export const ParentMessageInput$Ref: InputObjectRef = builder.inputRef< + ParentMessageInput$Shape +>("ParentMessageInput").implement({ + "fields": (t) => ({ + body: t.field({ + "type": "String", + "required": true, + "extensions": { "protobufField": { "name": "body", "typeFullName": "string" } }, + }), + nested: t.field({ + "type": ParentMessageNestedMessageInput$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "nested", "typeFullName": "testapis.nested.ParentMessage.NestedMessage" }, + }, + }), + nestedEnum: t.field({ + "type": ParentMessageNestedEnum$Ref, + "required": false, + "extensions": { + "protobufField": { "name": "nested_enum", "typeFullName": "testapis.nested.ParentMessage.NestedEnum" }, + }, + }), + }), + "extensions": { + "protobufMessage": { + "fullName": "testapis.nested.ParentMessage", + "name": "ParentMessage", + "package": "testapis.nested", + }, + }, +}); +" +`; + +exports[`createInputObjectTypeCode > with partial inputs > generates code for partial input types 1`] = ` +"import { InputObjectRef } from "@pothos/core"; +import { builder } from "../../../../builder"; + +export type MessagePartialInput$Shape = { + requiredPrimitives?: PrimitivesPartialInput$Shape | null; + optionalPrimitives?: PrimitivesPartialInput$Shape | null; + requiredPrimitivesList?: Array | null; + optionalPrimitivesList?: Array | null; +}; + +export const MessagePartialInput$Ref: InputObjectRef = builder.inputRef< + MessagePartialInput$Shape +>("MessagePartialInput").implement({ + "fields": (t) => ({ + requiredPrimitives: t.field({ + "type": PrimitivesPartialInput$Ref, + "required": false, + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitives: t.field({ + "type": PrimitivesPartialInput$Ref, + "required": false, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + requiredPrimitivesList: t.field({ + "type": [PrimitivesPartialInput$Ref], + "required": { "list": false, "items": true }, + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + optionalPrimitivesList: t.field({ + "type": [PrimitivesPartialInput$Ref], + "required": { "list": false, "items": true }, + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }), + }), + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, +}); +" +`; diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts new file mode 100644 index 00000000..4f5def1e --- /dev/null +++ b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts @@ -0,0 +1,199 @@ +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; +import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { + InputObjectType, + collectTypesFromFile, + createRegistryFromSchema, + defaultScalarMappingForTsProto, + defaultScalarMapping +} from "@proto-graphql/codegen-core"; +import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { code } from "ts-poet"; +import { describe, expect, test } from "vitest"; +import { createInputObjectTypeCode } from "./inputObjectType.js"; +import type { PothosPrinterOptions } from "./util.js"; + +// Helper to extract InputObjectType instances and generate code +function generateInputObjectTypeCode(packageName: string, typeName: string, options: PothosPrinterOptions) { + const req = buildCodeGeneratorRequest(packageName); + + // Default type options + const typeOptions = { + partialInputs: false, + scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + // Create a minimal plugin to get Schema + const plugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: createTsGenerator({ + generateFiles: (schema, file) => { + // Just return, we only need the schema + }, + dsl: "pothos", + }), + parseOptions: parsePothosOptions, + }); + + // Run the plugin to get the transformed schema + const resp = plugin.run(req); + + // Now we need to recreate the schema to access its internals + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + // Find the target file + const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); + if (!targetFile) throw new Error(`File for ${packageName} not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const inputType = types.find(t => t.typeName === typeName && t instanceof InputObjectType) as InputObjectType; + if (!inputType) throw new Error(`${typeName} type not found`); + + const generatedCode = createInputObjectTypeCode(inputType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parsePothosOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + return result; +} + +describe("createInputObjectTypeCode", () => { + describe("ts-proto", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "ts-proto" as const, + importPrefix: "@testapis/ts-proto", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for a simple input object", () => { + const code = generateInputObjectTypeCode("testapis.primitives", "PrimitivesInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an input object with nested fields", () => { + const code = generateInputObjectTypeCode("testapis.primitives", "MessageInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an input object with oneof fields", () => { + const code = generateInputObjectTypeCode("testapis.oneof", "OneofParentInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for empty input object", () => { + const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessageInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for nested input types", () => { + const code = generateInputObjectTypeCode("testapis.nested", "ParentMessageInput", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("protobuf-es", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "protobuf-es" as const, + importPrefix: "@testapis/protobuf-es", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for a simple input object", () => { + const code = generateInputObjectTypeCode("testapis.primitives", "PrimitivesInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an input object with nested fields", () => { + const code = generateInputObjectTypeCode("testapis.primitives", "MessageInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an input object with oneof fields", () => { + const code = generateInputObjectTypeCode("testapis.oneof", "OneofParentInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for empty input object", () => { + const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessageInput", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("with partial inputs", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "ts-proto" as const, + importPrefix: "@testapis/ts-proto", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for partial input types", () => { + const req = buildCodeGeneratorRequest("testapis.primitives"); + + // Create type options with partialInputs enabled + const typeOptions = { + partialInputs: true, + scalarMapping: defaultScalarMappingForTsProto, + ignoreNonMessageOneofFields: false, + }; + + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + const targetFile = schema.allFiles.find(f => f.name.includes("primitives")); + if (!targetFile) throw new Error(`File for primitives not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const partialInputType = types.find(t => t.typeName === "MessagePartialInput" && t instanceof InputObjectType) as InputObjectType; + if (!partialInputType) throw new Error(`MessagePartialInput type not found`); + + const generatedCode = createInputObjectTypeCode(partialInputType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parsePothosOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + + expect(result).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file From d00f7842e7555ddb1569abf0214f88eaf00e06e0 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sat, 7 Jun 2025 09:35:34 +0900 Subject: [PATCH 04/27] test(protoc-gen-pothos): add snapshot tests for oneofUnionType printer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tests for createOneofUnionTypeCode function - Test required and optional oneof unions - Test squashed oneof unions - Test imported oneof members - Support both ts-proto and protobuf-es libraries - Test different file layouts (proto_file vs graphql_type) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../__snapshots__/oneofUnionType.test.ts.snap | 189 ++++++++++++++++++ .../dslgen/printers/oneofUnionType.test.ts | 164 +++++++++++++++ 2 files changed, 353 insertions(+) create mode 100644 packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap create mode 100644 packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap new file mode 100644 index 00000000..2c5e93ab --- /dev/null +++ b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap @@ -0,0 +1,189 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createOneofUnionTypeCode > protobuf-es > generates code for a required oneof union 1`] = ` +"import { builder } from "../../../../builder"; + +export const OneofParentRequiredOneofMembers$Ref = builder.unionType("OneofParentRequiredOneofMembers", { + "types": [OneofMemberMessage1$Ref, OneofMemberMessage2$Ref], + "description": "Required. disallow not_set.", + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.required_oneof_members", + "name": "required_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "required_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "required_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > protobuf-es > generates code for a squashed oneof union 1`] = ` +"import { builder } from "../../../../builder"; + +export const TestPrefixPrefixedMessageSquashedMessage$Ref = builder.unionType( + "TestPrefixPrefixedMessageSquashedMessage", + { + "types": [TestPrefixPrefixedMessageInnerMessage$Ref, TestPrefixPrefixedMessageInnerMessage2$Ref], + "extensions": { + "protobufOneof": { + "fullName": "testapis.extensions.PrefixedMessage.SquashedMessage", + "name": "SquashedMessage", + "package": "testapis.extensions", + "fields": [{ + "name": "oneof_field", + "type": "testapis.extensions.PrefixedMessage.InnerMessage", + "options": { "[graphql.object_type]": { "squashUnion": true } }, + }, { + "name": "oneof_field_2", + "type": "testapis.extensions.PrefixedMessage.InnerMessage2", + "options": { "[graphql.object_type]": { "squashUnion": true } }, + }], + }, + }, + }, +); +" +`; + +exports[`createOneofUnionTypeCode > protobuf-es > generates code for an optional oneof union 1`] = ` +"import { builder } from "../../../../builder"; + +export const OneofParentOptionalOneofMembers$Ref = builder.unionType("OneofParentOptionalOneofMembers", { + "types": [OneofMemberMessage1$Ref, OneofMemberMessage2$Ref], + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.optional_oneof_members", + "name": "optional_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "optoinal_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "optoinal_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > ts-proto > generates code for a required oneof union 1`] = ` +"import { builder } from "../../../../builder"; + +export const OneofParentRequiredOneofMembers$Ref = builder.unionType("OneofParentRequiredOneofMembers", { + "types": [OneofMemberMessage1$Ref, OneofMemberMessage2$Ref], + "description": "Required. disallow not_set.", + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.required_oneof_members", + "name": "required_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "required_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "required_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > ts-proto > generates code for a squashed oneof union 1`] = ` +"import { builder } from "../../../../builder"; + +export const TestPrefixPrefixedMessageSquashedMessage$Ref = builder.unionType( + "TestPrefixPrefixedMessageSquashedMessage", + { + "types": [TestPrefixPrefixedMessageInnerMessage$Ref, TestPrefixPrefixedMessageInnerMessage2$Ref], + "extensions": { + "protobufOneof": { + "fullName": "testapis.extensions.PrefixedMessage.SquashedMessage", + "name": "SquashedMessage", + "package": "testapis.extensions", + "fields": [{ + "name": "oneof_field", + "type": "testapis.extensions.PrefixedMessage.InnerMessage", + "options": { "[graphql.object_type]": { "squashUnion": true } }, + }, { + "name": "oneof_field_2", + "type": "testapis.extensions.PrefixedMessage.InnerMessage2", + "options": { "[graphql.object_type]": { "squashUnion": true } }, + }], + }, + }, + }, +); +" +`; + +exports[`createOneofUnionTypeCode > ts-proto > generates code for an optional oneof union 1`] = ` +"import { builder } from "../../../../builder"; + +export const OneofParentOptionalOneofMembers$Ref = builder.unionType("OneofParentOptionalOneofMembers", { + "types": [OneofMemberMessage1$Ref, OneofMemberMessage2$Ref], + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.optional_oneof_members", + "name": "optional_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "optoinal_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "optoinal_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > ts-proto > generates code for imported oneof member 1`] = ` +"import { builder } from "../../../../../builder"; +import { OneofMember1$Ref, OneofMember2$Ref } from "./member.pothos"; + +export const OneofParentOneofField$Ref = builder.unionType("OneofParentOneofField", { + "types": [OneofMember1$Ref, OneofMember2$Ref], + "extensions": { + "protobufOneof": { + "fullName": "testapis.edgecases.import_oneof_member_from_other_file.OneofParent.oneof_field", + "name": "oneof_field", + "messageName": "OneofParent", + "package": "testapis.edgecases.import_oneof_member_from_other_file", + "fields": [{ "name": "member1", "type": "testapis.edgecases.import_oneof_member_from_other_file.OneofMember1" }, { + "name": "member2", + "type": "testapis.edgecases.import_oneof_member_from_other_file.OneofMember2", + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > with file layout graphql_type > generates code with correct imports for graphql_type layout 1`] = ` +"import { builder } from "../../../../builder"; +import { OneofMemberMessage1$Ref } from "./OneofMemberMessage1.pothos"; +import { OneofMemberMessage2$Ref } from "./OneofMemberMessage2.pothos"; + +export const OneofParentRequiredOneofMembers$Ref = builder.unionType("OneofParentRequiredOneofMembers", { + "types": [OneofMemberMessage1$Ref, OneofMemberMessage2$Ref], + "description": "Required. disallow not_set.", + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.required_oneof_members", + "name": "required_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "required_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "required_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts new file mode 100644 index 00000000..f734c0d9 --- /dev/null +++ b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts @@ -0,0 +1,164 @@ +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; +import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { + OneofUnionType, + SquashedOneofUnionType, + collectTypesFromFile, + createRegistryFromSchema, + defaultScalarMappingForTsProto, + defaultScalarMapping +} from "@proto-graphql/codegen-core"; +import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { code } from "ts-poet"; +import { describe, expect, test } from "vitest"; +import { createOneofUnionTypeCode } from "./oneofUnionType.js"; +import type { PothosPrinterOptions } from "./util.js"; + +// Helper to extract OneofUnionType instances and generate code +function generateOneofUnionTypeCode(packageName: string, typeName: string, options: PothosPrinterOptions) { + const req = buildCodeGeneratorRequest(packageName); + + // Default type options + const typeOptions = { + partialInputs: false, + scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + // Create a minimal plugin to get Schema + const plugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: createTsGenerator({ + generateFiles: (schema, file) => { + // Just return, we only need the schema + }, + dsl: "pothos", + }), + parseOptions: parsePothosOptions, + }); + + // Run the plugin to get the transformed schema + const resp = plugin.run(req); + + // Now we need to recreate the schema to access its internals + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + // Find the target file + let targetFile; + + // For the import_oneof_member_from_other_file test, we need to check the parent.proto file + if (packageName === "testapis.edgecases.import_oneof_member_from_other_file") { + targetFile = schema.allFiles.find(f => f.name.includes("import_oneof_member_from_other_file/parent")); + } else { + targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); + } + + if (!targetFile) throw new Error(`File for ${packageName} not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const oneofType = types.find(t => t.typeName === typeName && (t instanceof OneofUnionType || t instanceof SquashedOneofUnionType)); + if (!oneofType) throw new Error(`${typeName} type not found`); + + const generatedCode = createOneofUnionTypeCode(oneofType as OneofUnionType | SquashedOneofUnionType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parsePothosOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + return result; +} + +describe("createOneofUnionTypeCode", () => { + describe("ts-proto", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "ts-proto" as const, + importPrefix: "@testapis/ts-proto", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for a required oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an optional oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentOptionalOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a squashed oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.extensions", "TestPrefixPrefixedMessageSquashedMessage", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for imported oneof member", () => { + const code = generateOneofUnionTypeCode("testapis.edgecases.import_oneof_member_from_other_file", "OneofParentOneofField", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("protobuf-es", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "protobuf-es" as const, + importPrefix: "@testapis/protobuf-es", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code for a required oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an optional oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentOptionalOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a squashed oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.extensions", "TestPrefixPrefixedMessageSquashedMessage", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("with file layout graphql_type", () => { + const options: PothosPrinterOptions = { + dsl: "pothos", + protobuf: "ts-proto" as const, + importPrefix: "@testapis/ts-proto", + emitImportedFiles: false, + fileLayout: "graphql_type", + filenameSuffix: ".pothos", + pothos: { + builderPath: "../../builder", + }, + }; + + test("generates code with correct imports for graphql_type layout", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file From 2a8359b1253f4f9b4e2d381b16913359cee2eb36 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sat, 7 Jun 2025 09:38:05 +0900 Subject: [PATCH 05/27] test(protoc-gen-nexus): add snapshot tests for objectType printer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tests for createObjectTypeCode function - Test simple messages, nested fields, oneofs, empty messages - Support google-protobuf and protobufjs libraries - Include interface type generation - Test messages with GraphQL extensions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../__snapshots__/objectType.test.ts.snap | 1022 +++++++++++++++++ .../src/dslgen/printers/objectType.test.ts | 136 +++ 2 files changed, 1158 insertions(+) create mode 100644 packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/objectType.test.ts.snap create mode 100644 packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/objectType.test.ts.snap b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/objectType.test.ts.snap new file mode 100644 index 00000000..d120a355 --- /dev/null +++ b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/objectType.test.ts.snap @@ -0,0 +1,1022 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createObjectTypeCode > google-protobuf > generates code for a message with nested fields 1`] = ` +"import { Message as Message1 } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; +import { list, nonNull, nullable, objectType } from "nexus"; + +export type testapis$primitives$Message = Message1; +export const Message = objectType({ + "name": "Message", + "definition": (t) => { + t.field("requiredPrimitives", { + "type": nonNull("Primitives"), + "description": "Required.", + "resolve": (source) => { + const value = source.getRequiredPrimitives(); + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitives", { + "type": nullable("Primitives"), + "description": "Optional.", + "resolve": (source) => { + const value = source.getOptionalPrimitives(); + if (value == null) { + return null; + } + return value; + }, + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("requiredPrimitivesList", { + "type": nonNull(list(nonNull("Primitives"))), + "description": "Required.", + "resolve": (source) => { + return source.getRequiredPrimitivesListList().map((value) => { + return value; + }); + }, + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitivesList", { + "type": nullable(list(nonNull("Primitives"))), + "description": "Optional.", + "resolve": (source) => { + return source.getOptionalPrimitivesListList().map((value) => { + return value; + }); + }, + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + }, + "isTypeOf": (data: unknown) => { + return data instanceof Message1; + }, + "sourceType": { "module": __filename, "export": "testapis$primitives$Message" }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > google-protobuf > generates code for a simple message 1`] = ` +"import { Primitives as Primitives1 } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; +import { list, nonNull, objectType } from "nexus"; + +export type testapis$primitives$Primitives = Primitives1; +export const Primitives = objectType({ + "name": "Primitives", + "definition": (t) => { + t.field("requiredDoubleValue", { + "type": nonNull("Float"), + "resolve": (source) => { + const value = source.getRequiredDoubleValue(); + return value; + }, + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }); + t.field("requiredFloatValue", { + "type": nonNull("Float"), + "resolve": (source) => { + const value = source.getRequiredFloatValue(); + return value; + }, + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }); + t.field("requiredInt32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.getRequiredInt32Value(); + return value; + }, + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }); + t.field("requiredInt64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.getRequiredInt64Value(); + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }); + t.field("requiredUint32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.getRequiredUint32Value(); + return value; + }, + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }); + t.field("requiredUint64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.getRequiredUint64Value(); + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }); + t.field("requiredSint32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.getRequiredSint32Value(); + return value; + }, + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }); + t.field("requiredSint64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.getRequiredSint64Value(); + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }); + t.field("requiredFixed32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.getRequiredFixed32Value(); + return value; + }, + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }); + t.field("requiredFixed64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.getRequiredFixed64Value(); + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }); + t.field("requiredSfixed32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.getRequiredSfixed32Value(); + return value; + }, + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }); + t.field("requiredSfixed64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.getRequiredSfixed64Value(); + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }); + t.field("requiredBoolValue", { + "type": nonNull("Boolean"), + "resolve": (source) => { + const value = source.getRequiredBoolValue(); + return value; + }, + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }); + t.field("requiredStringValue", { + "type": nonNull("String"), + "resolve": (source) => { + const value = source.getRequiredStringValue(); + return value; + }, + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }); + t.field("requiredBytesValue", { + "type": nonNull("Byte"), + "resolve": (source) => { + const value = source.getRequiredBytesValue(); + return value; + }, + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }); + t.field("requiredDoubleValues", { + "type": nonNull(list(nonNull("Float"))), + "resolve": (source) => { + return source.getRequiredDoubleValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }); + t.field("requiredFloatValues", { + "type": nonNull(list(nonNull("Float"))), + "resolve": (source) => { + return source.getRequiredFloatValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }); + t.field("requiredInt32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.getRequiredInt32ValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }); + t.field("requiredInt64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.getRequiredInt64ValuesList().map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }); + t.field("requiredUint32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.getRequiredUint32ValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }); + t.field("requiredUint64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.getRequiredUint64ValuesList().map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }); + t.field("requiredSint32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.getRequiredSint32ValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }); + t.field("requiredSint64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.getRequiredSint64ValuesList().map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }); + t.field("requiredFixed32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.getRequiredFixed32ValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }); + t.field("requiredFixed64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.getRequiredFixed64ValuesList().map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }); + t.field("requiredSfixed32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.getRequiredSfixed32ValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }); + t.field("requiredSfixed64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.getRequiredSfixed64ValuesList().map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }); + t.field("requiredBoolValues", { + "type": nonNull(list(nonNull("Boolean"))), + "resolve": (source) => { + return source.getRequiredBoolValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }); + t.field("requiredStringValues", { + "type": nonNull(list(nonNull("String"))), + "resolve": (source) => { + return source.getRequiredStringValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }); + t.field("requiredBytesValues", { + "type": nonNull(list(nonNull("Byte"))), + "resolve": (source) => { + return source.getRequiredBytesValuesList().map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }); + }, + "isTypeOf": (data: unknown) => { + return data instanceof Primitives1; + }, + "sourceType": { "module": __filename, "export": "testapis$primitives$Primitives" }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Primitives", + "name": "Primitives", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > protobufjs > generates code for a message with nested fields 1`] = ` +"import { testapis } from "@testapis/protobufjs/testapis/primitives"; +import { list, nonNull, nullable, objectType } from "nexus"; + +export type testapis$primitives$Message = testapis.primitives.Message; +export const Message = objectType({ + "name": "Message", + "definition": (t) => { + t.field("requiredPrimitives", { + "type": nonNull("Primitives"), + "description": "Required.", + "resolve": (source) => { + const value = source.requiredPrimitives; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitives", { + "type": nullable("Primitives"), + "description": "Optional.", + "resolve": (source) => { + const value = source.optionalPrimitives; + if (value == null) { + return null; + } + return value; + }, + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("requiredPrimitivesList", { + "type": nonNull(list(nonNull("Primitives"))), + "description": "Required.", + "resolve": (source) => { + return source.requiredPrimitivesList.map((value) => { + return value; + }); + }, + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitivesList", { + "type": nullable(list(nonNull("Primitives"))), + "description": "Optional.", + "resolve": (source) => { + return source.optionalPrimitivesList.map((value) => { + return value; + }); + }, + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + }, + "isTypeOf": (data: unknown) => { + return data instanceof testapis.primitives.Message; + }, + "sourceType": { "module": __filename, "export": "testapis$primitives$Message" }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > protobufjs > generates code for a message with oneofs 1`] = ` +"import { testapis } from "@testapis/protobufjs/testapis/oneof"; +import { nonNull, nullable, objectType } from "nexus"; + +export type testapis$oneof$OneofParent = testapis.oneof.OneofParent; +export const OneofParent = objectType({ + "name": "OneofParent", + "definition": (t) => { + t.field("normalField", { + "type": nonNull("String"), + "resolve": (source) => { + const value = source.normalField; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }); + t.field("requiredOneofMembers", { + "type": nonNull("OneofParentRequiredOneofMembers"), + "description": "Required. disallow not_set.", + "resolve": (source) => { + const value = source; + if (value.requiredMessage1 != null) { + return value.requiredMessage1; + } + if (value.requiredMessage2 != null) { + return value.requiredMessage2; + } + throw new Error("One of the following fields must be non-null: required_message1, required_message2"); + }, + "extensions": { "protobufField": { "name": "required_oneof_members" } }, + }); + t.field("optionalOneofMembers", { + "type": nullable("OneofParentOptionalOneofMembers"), + "resolve": (source) => { + const value = source; + if (value.optoinalMessage1 != null) { + return value.optoinalMessage1; + } + if (value.optoinalMessage2 != null) { + return value.optoinalMessage2; + } + return null; + }, + "extensions": { "protobufField": { "name": "optional_oneof_members" } }, + }); + }, + "isTypeOf": (data: unknown) => { + return data instanceof testapis.oneof.OneofParent; + }, + "sourceType": { "module": __filename, "export": "testapis$oneof$OneofParent" }, + "extensions": { + "protobufMessage": { "fullName": "testapis.oneof.OneofParent", "name": "OneofParent", "package": "testapis.oneof" }, + }, +}); +" +`; + +exports[`createObjectTypeCode > protobufjs > generates code for a simple message 1`] = ` +"import { testapis } from "@testapis/protobufjs/testapis/primitives"; +import { list, nonNull, objectType } from "nexus"; + +export type testapis$primitives$Primitives = testapis.primitives.Primitives; +export const Primitives = objectType({ + "name": "Primitives", + "definition": (t) => { + t.field("requiredDoubleValue", { + "type": nonNull("Float"), + "resolve": (source) => { + const value = source.requiredDoubleValue; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }); + t.field("requiredFloatValue", { + "type": nonNull("Float"), + "resolve": (source) => { + const value = source.requiredFloatValue; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }); + t.field("requiredInt32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.requiredInt32Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }); + t.field("requiredInt64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.requiredInt64Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }); + t.field("requiredUint32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.requiredUint32Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }); + t.field("requiredUint64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.requiredUint64Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }); + t.field("requiredSint32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.requiredSint32Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }); + t.field("requiredSint64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.requiredSint64Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }); + t.field("requiredFixed32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.requiredFixed32Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }); + t.field("requiredFixed64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.requiredFixed64Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }); + t.field("requiredSfixed32Value", { + "type": nonNull("Int"), + "resolve": (source) => { + const value = source.requiredSfixed32Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }); + t.field("requiredSfixed64Value", { + "type": nonNull("Int64"), + "resolve": (source) => { + const value = source.requiredSfixed64Value; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value.toString(); + }, + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }); + t.field("requiredBoolValue", { + "type": nonNull("Boolean"), + "resolve": (source) => { + const value = source.requiredBoolValue; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }); + t.field("requiredStringValue", { + "type": nonNull("String"), + "resolve": (source) => { + const value = source.requiredStringValue; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }); + t.field("requiredBytesValue", { + "type": nonNull("Byte"), + "resolve": (source) => { + const value = source.requiredBytesValue; + if (value == null) { + throw new Error("Cannot return null for non-nullable field"); + } + return value; + }, + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }); + t.field("requiredDoubleValues", { + "type": nonNull(list(nonNull("Float"))), + "resolve": (source) => { + return source.requiredDoubleValues.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }); + t.field("requiredFloatValues", { + "type": nonNull(list(nonNull("Float"))), + "resolve": (source) => { + return source.requiredFloatValues.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }); + t.field("requiredInt32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.requiredInt32Values.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }); + t.field("requiredInt64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.requiredInt64Values.map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }); + t.field("requiredUint32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.requiredUint32Values.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }); + t.field("requiredUint64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.requiredUint64Values.map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }); + t.field("requiredSint32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.requiredSint32Values.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }); + t.field("requiredSint64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.requiredSint64Values.map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }); + t.field("requiredFixed32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.requiredFixed32Values.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }); + t.field("requiredFixed64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.requiredFixed64Values.map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }); + t.field("requiredSfixed32Values", { + "type": nonNull(list(nonNull("Int"))), + "resolve": (source) => { + return source.requiredSfixed32Values.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }); + t.field("requiredSfixed64Values", { + "type": nonNull(list(nonNull("Int64"))), + "resolve": (source) => { + return source.requiredSfixed64Values.map((value) => { + return value.toString(); + }); + }, + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }); + t.field("requiredBoolValues", { + "type": nonNull(list(nonNull("Boolean"))), + "resolve": (source) => { + return source.requiredBoolValues.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }); + t.field("requiredStringValues", { + "type": nonNull(list(nonNull("String"))), + "resolve": (source) => { + return source.requiredStringValues.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }); + t.field("requiredBytesValues", { + "type": nonNull(list(nonNull("Byte"))), + "resolve": (source) => { + return source.requiredBytesValues.map((value) => { + return value; + }); + }, + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }); + }, + "isTypeOf": (data: unknown) => { + return data instanceof testapis.primitives.Primitives; + }, + "sourceType": { "module": __filename, "export": "testapis$primitives$Primitives" }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Primitives", + "name": "Primitives", + "package": "testapis.primitives", + }, + }, +}); +" +`; + +exports[`createObjectTypeCode > with extensions > generates code for message with field extensions 1`] = ` +"import { + EnumWillRename, + PrefixedEnum, + PrefixedMessage, +} from "@testapis/google-protobuf/testapis/extensions/extensions_pb"; +import { list, nonNull, nullable, objectType } from "nexus"; + +export type testapis$extensions$PrefixedMessage = PrefixedMessage; +export const TestPrefixPrefixedMessage = objectType({ + "name": "TestPrefixPrefixedMessage", + "definition": (t) => { + t.field("id", { + "type": nonNull("Int64"), + "description": "Output only.", + "resolve": (source) => { + const value = source.getId(); + return value.toString(); + }, + "extensions": { "protobufField": { "name": "id", "typeFullName": "uint64" } }, + }); + t.field("body", { + "type": nonNull("String"), + "resolve": (source) => { + const value = source.getBody(); + return value; + }, + "extensions": { "protobufField": { "name": "body", "typeFullName": "string" } }, + }); + t.field("prefixedEnum", { + "type": nullable("TestPrefixPrefixedEnum"), + "resolve": (source) => { + const value = source.getPrefixedEnum(); + if (value == null) { + return null; + } + if (value === PrefixedEnum.PREFIXED_ENUM_UNSPECIFIED) { + return null; + } + if (value === PrefixedEnum.PREFIXED_IGNORED) { + return null; + } + return value; + }, + "extensions": { + "protobufField": { "name": "prefixed_enum", "typeFullName": "testapis.extensions.PrefixedEnum" }, + }, + }); + t.field("notIgnoredMessage", { + "type": nullable("TestPrefixIgnoredMessageNotIgnored"), + "resolve": (source) => { + const value = source.getNotIgnoredMessage(); + if (value == null) { + return null; + } + return value; + }, + "extensions": { + "protobufField": { + "name": "not_ignored_message", + "typeFullName": "testapis.extensions.IgnoredMessage.NotIgnored", + }, + }, + }); + t.field("squashedMessage", { + "type": nullable("TestPrefixPrefixedMessageSquashedMessage"), + "resolve": (source) => { + const value = source.getSquashedMessage(); + if (value == null) { + return null; + } + switch (value.getSquashedMessageCase()) { + case PrefixedMessage.SquashedMessage.SquashedMessageCase.ONEOF_FIELD: { + return value.getOneofField()!; + } + + case PrefixedMessage.SquashedMessage.SquashedMessageCase.ONEOF_FIELD_2: { + return value.getOneofField2()!; + } + + default: { + return null; + } + } + }, + "extensions": { + "protobufField": { + "name": "squashed_message", + "typeFullName": "testapis.extensions.PrefixedMessage.SquashedMessage", + }, + }, + }); + t.field("thisFieldWasRenamed", { + "type": nonNull("String"), + "resolve": (source) => { + const value = source.getThisFieldWillBeRenamed(); + return value; + }, + "extensions": { + "protobufField": { + "name": "this_field_will_be_renamed", + "typeFullName": "string", + "options": { "[graphql.field]": { "name": "thisFieldWasRenamed" } }, + }, + }, + }); + t.field("skipResolver", { + "type": nonNull("String"), + "resolve": (source) => { + throw new Error("not implemented"); + }, + "extensions": { + "protobufField": { + "name": "skip_resolver", + "typeFullName": "string", + "options": { "[graphql.field]": { "skipResolver": true } }, + }, + }, + }); + t.field("squashedMessages", { + "type": nullable(list(nonNull("TestPrefixPrefixedMessageSquashedMessage"))), + "resolve": (source) => { + return source.getSquashedMessagesList().map((value) => { + switch (value.getSquashedMessageCase()) { + case PrefixedMessage.SquashedMessage.SquashedMessageCase.ONEOF_FIELD: { + return value.getOneofField()!; + } + + case PrefixedMessage.SquashedMessage.SquashedMessageCase.ONEOF_FIELD_2: { + return value.getOneofField2()!; + } + + default: { + throw new Error("One of the following fields must be non-null: oneof_field, oneof_field_2"); + } + } + }); + }, + "extensions": { + "protobufField": { + "name": "squashed_messages", + "typeFullName": "testapis.extensions.PrefixedMessage.SquashedMessage", + }, + }, + }); + t.field("renamedMessage", { + "type": nullable("TestPrefixRenamedMessage"), + "resolve": (source) => { + const value = source.getRenamedMessage(); + if (value == null) { + return null; + } + return value; + }, + "extensions": { + "protobufField": { "name": "renamed_message", "typeFullName": "testapis.extensions.MessageWillRename" }, + }, + }); + t.field("renamedEnum", { + "type": nullable("TestPrefixRenamedEnum"), + "resolve": (source) => { + const value = source.getRenamedEnum(); + if (value == null) { + return null; + } + if (value === EnumWillRename.ENUM_WILL_RENAME_UNSPECIFIED) { + return null; + } + return value; + }, + "extensions": { + "protobufField": { "name": "renamed_enum", "typeFullName": "testapis.extensions.EnumWillRename" }, + }, + }); + t.field("partialIgnoreOneof", { + "type": nullable("TestPrefixPrefixedMessagePartialIgnoreOneof"), + "resolve": (source) => { + const value = source; + switch (value.getPartialIgnoreOneofCase()) { + case PrefixedMessage.PartialIgnoreOneofCase.ONEOF_NOT_IGNORED_FIELD: { + return value.getOneofNotIgnoredField()!; + } + + default: { + return null; + } + } + }, + "extensions": { "protobufField": { "name": "partial_ignore_oneof" } }, + }); + }, + "isTypeOf": (data: unknown) => { + return data instanceof PrefixedMessage; + }, + "sourceType": { "module": __filename, "export": "testapis$extensions$PrefixedMessage" }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.extensions.PrefixedMessage", + "name": "PrefixedMessage", + "package": "testapis.extensions", + }, + }, +}); +" +`; diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts new file mode 100644 index 00000000..9f6c54b5 --- /dev/null +++ b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts @@ -0,0 +1,136 @@ +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; +import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { + ObjectType, + collectTypesFromFile, + createRegistryFromSchema, + defaultScalarMappingForTsProto, + defaultScalarMapping +} from "@proto-graphql/codegen-core"; +import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { code } from "ts-poet"; +import { describe, expect, test } from "vitest"; +import { createObjectTypeCode } from "./objectType.js"; +import type { NexusPrinterOptions } from "./util.js"; + +// Helper to extract ObjectType instances and generate code +function generateObjectTypeCode(packageName: string, messageTypeName: string, options: NexusPrinterOptions) { + const req = buildCodeGeneratorRequest(packageName); + + // Default type options + const typeOptions = { + partialInputs: false, + scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + // Create a minimal plugin to get Schema + const plugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: createTsGenerator({ + generateFiles: (schema, file) => { + // Just return, we only need the schema + }, + dsl: "nexus", + }), + parseOptions: parseNexusOptions, + }); + + // Run the plugin to get the transformed schema + const resp = plugin.run(req); + + // Now we need to recreate the schema to access its internals + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + // Find the target file + const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); + if (!targetFile) throw new Error(`File for ${packageName} not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const objectType = types.find(t => t.typeName === messageTypeName && t instanceof ObjectType) as ObjectType; + if (!objectType) throw new Error(`${messageTypeName} type not found`); + + const generatedCode = createObjectTypeCode(objectType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parseNexusOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + return result; +} + +describe("createObjectTypeCode", () => { + describe("google-protobuf", () => { + const options: NexusPrinterOptions = { + dsl: "nexus", + protobuf: "google-protobuf" as const, + importPrefix: "@testapis/google-protobuf", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".nexus", + }; + + test("generates code for a simple message", () => { + const code = generateObjectTypeCode("testapis.primitives", "Primitives", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a message with nested fields", () => { + const code = generateObjectTypeCode("testapis.primitives", "Message", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("protobufjs", () => { + const options: NexusPrinterOptions = { + dsl: "nexus", + protobuf: "protobufjs" as const, + importPrefix: "@testapis/protobufjs", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".nexus", + }; + + test("generates code for a simple message", () => { + const code = generateObjectTypeCode("testapis.primitives", "Primitives", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a message with nested fields", () => { + const code = generateObjectTypeCode("testapis.primitives", "Message", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for a message with oneofs", () => { + const code = generateObjectTypeCode("testapis.oneof", "OneofParent", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("with extensions", () => { + const options: NexusPrinterOptions = { + dsl: "nexus", + protobuf: "google-protobuf" as const, + importPrefix: "@testapis/google-protobuf", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".nexus", + }; + + test("generates code for message with field extensions", () => { + const code = generateObjectTypeCode("testapis.extensions", "TestPrefixPrefixedMessage", options); + expect(code).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file From 79b69db5ce1b63adfdfd842ca3b7285bb6c88894 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sat, 7 Jun 2025 09:39:41 +0900 Subject: [PATCH 06/27] test(protoc-gen-nexus): add snapshot tests for enumType printer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tests for createEnumTypeCode function - Test simple enums, enums without unspecified value - Test nested enums and enums with extensions - Support google-protobuf and protobufjs libraries 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../__snapshots__/enumType.test.ts.snap | 157 ++++++++++++++++++ .../src/dslgen/printers/enumType.test.ts | 111 +++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/enumType.test.ts.snap create mode 100644 packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/enumType.test.ts.snap b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/enumType.test.ts.snap new file mode 100644 index 00000000..4881e999 --- /dev/null +++ b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/enumType.test.ts.snap @@ -0,0 +1,157 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createEnumTypeCode > google-protobuf > generates code for a simple enum 1`] = ` +"import { enumType } from "nexus"; + +export const MyEnum = enumType({ + "name": "MyEnum", + "members": [{ "name": "FOO", "value": 1, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_FOO" } } }, { + "name": "BAR", + "value": 2, + "description": "This is Bar.", + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_BAR" } }, + }, { "name": "BAZ", "value": 3, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_BAZ" } } }], + "extensions": { + "protobufEnum": { "name": "MyEnum", "fullName": "testapi.enums.MyEnum", "package": "testapi.enums" }, + }, +}); +" +`; + +exports[`createEnumTypeCode > google-protobuf > generates code for an enum without unspecified 1`] = ` +"import { enumType } from "nexus"; + +export const MyEnumWithoutUnspecified = enumType({ + "name": "MyEnumWithoutUnspecified", + "members": [{ + "name": "FOO", + "value": 0, + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_FOO" } }, + }, { + "name": "BAR", + "value": 1, + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_BAR" } }, + }, { + "name": "BAZ", + "value": 2, + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_BAZ" } }, + }], + "extensions": { + "protobufEnum": { + "name": "MyEnumWithoutUnspecified", + "fullName": "testapi.enums.MyEnumWithoutUnspecified", + "package": "testapi.enums", + }, + }, +}); +" +`; + +exports[`createEnumTypeCode > google-protobuf > generates code for enum with extensions 1`] = ` +"import { enumType } from "nexus"; + +export const TestPrefixPrefixedEnum = enumType({ + "name": "TestPrefixPrefixedEnum", + "members": [ + { "name": "PREFIXED_FOO", "value": 1, "extensions": { "protobufEnumValue": { "name": "PREFIXED_FOO" } } }, + { "name": "PREFIXED_BAR", "value": 2, "extensions": { "protobufEnumValue": { "name": "PREFIXED_BAR" } } }, + ], + "extensions": { + "protobufEnum": { + "name": "PrefixedEnum", + "fullName": "testapis.extensions.PrefixedEnum", + "package": "testapis.extensions", + }, + }, +}); +" +`; + +exports[`createEnumTypeCode > google-protobuf > generates code for nested enum 1`] = ` +"import { enumType } from "nexus"; + +export const ParentMessageNestedEnum = enumType({ + "name": "ParentMessageNestedEnum", + "members": [{ "name": "FOO", "value": 1, "extensions": { "protobufEnumValue": { "name": "FOO" } } }, { + "name": "BAR", + "value": 2, + "extensions": { "protobufEnumValue": { "name": "BAR" } }, + }], + "extensions": { + "protobufEnum": { + "name": "NestedEnum", + "fullName": "testapis.nested.ParentMessage.NestedEnum", + "package": "testapis.nested", + }, + }, +}); +" +`; + +exports[`createEnumTypeCode > protobufjs > generates code for a simple enum 1`] = ` +"import { enumType } from "nexus"; + +export const MyEnum = enumType({ + "name": "MyEnum", + "members": [{ "name": "FOO", "value": 1, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_FOO" } } }, { + "name": "BAR", + "value": 2, + "description": "This is Bar.", + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_BAR" } }, + }, { "name": "BAZ", "value": 3, "extensions": { "protobufEnumValue": { "name": "MY_ENUM_BAZ" } } }], + "extensions": { + "protobufEnum": { "name": "MyEnum", "fullName": "testapi.enums.MyEnum", "package": "testapi.enums" }, + }, +}); +" +`; + +exports[`createEnumTypeCode > protobufjs > generates code for an enum without unspecified 1`] = ` +"import { enumType } from "nexus"; + +export const MyEnumWithoutUnspecified = enumType({ + "name": "MyEnumWithoutUnspecified", + "members": [{ + "name": "FOO", + "value": 0, + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_FOO" } }, + }, { + "name": "BAR", + "value": 1, + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_BAR" } }, + }, { + "name": "BAZ", + "value": 2, + "extensions": { "protobufEnumValue": { "name": "MY_ENUM_WITHOUT_UNSPECIFIED_BAZ" } }, + }], + "extensions": { + "protobufEnum": { + "name": "MyEnumWithoutUnspecified", + "fullName": "testapi.enums.MyEnumWithoutUnspecified", + "package": "testapi.enums", + }, + }, +}); +" +`; + +exports[`createEnumTypeCode > protobufjs > generates code for nested enum 1`] = ` +"import { enumType } from "nexus"; + +export const ParentMessageNestedEnum = enumType({ + "name": "ParentMessageNestedEnum", + "members": [{ "name": "FOO", "value": 1, "extensions": { "protobufEnumValue": { "name": "FOO" } } }, { + "name": "BAR", + "value": 2, + "extensions": { "protobufEnumValue": { "name": "BAR" } }, + }], + "extensions": { + "protobufEnum": { + "name": "NestedEnum", + "fullName": "testapis.nested.ParentMessage.NestedEnum", + "package": "testapis.nested", + }, + }, +}); +" +`; diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts new file mode 100644 index 00000000..1ec33c98 --- /dev/null +++ b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts @@ -0,0 +1,111 @@ +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; +import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { + EnumType, + collectTypesFromFile, + createRegistryFromSchema, + defaultScalarMappingForTsProto, + defaultScalarMapping +} from "@proto-graphql/codegen-core"; +import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { code } from "ts-poet"; +import { describe, expect, test } from "vitest"; +import { createEnumTypeCode } from "./enumType.js"; + +// Helper to extract EnumType instances and generate code +function generateEnumTypeCode(packageName: string, enumTypeName: string, protobuf: "google-protobuf" | "protobufjs") { + const req = buildCodeGeneratorRequest(packageName); + + // Default type options + const typeOptions = { + partialInputs: false, + scalarMapping: defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + // Create a minimal plugin to get Schema + const plugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: createTsGenerator({ + generateFiles: (schema, file) => { + // Just return, we only need the schema + }, + dsl: "nexus", + }), + parseOptions: parseNexusOptions, + }); + + // Run the plugin to get the transformed schema + const resp = plugin.run(req); + + // Now we need to recreate the schema to access its internals + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + // Find the target file + const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); + if (!targetFile) throw new Error(`File for ${packageName} not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const enumType = types.find(t => t.typeName === enumTypeName && t instanceof EnumType) as EnumType; + if (!enumType) throw new Error(`${enumTypeName} type not found`); + + const generatedCode = createEnumTypeCode(enumType, registry); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parseNexusOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + return result; +} + +describe("createEnumTypeCode", () => { + describe("google-protobuf", () => { + test("generates code for a simple enum", () => { + const code = generateEnumTypeCode("testapis.enums", "MyEnum", "google-protobuf"); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an enum without unspecified", () => { + const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified", "google-protobuf"); + expect(code).toMatchSnapshot(); + }); + + test("generates code for nested enum", () => { + const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum", "google-protobuf"); + expect(code).toMatchSnapshot(); + }); + + test("generates code for enum with extensions", () => { + const code = generateEnumTypeCode("testapis.extensions", "TestPrefixPrefixedEnum", "google-protobuf"); + expect(code).toMatchSnapshot(); + }); + }); + + describe("protobufjs", () => { + test("generates code for a simple enum", () => { + const code = generateEnumTypeCode("testapis.enums", "MyEnum", "protobufjs"); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an enum without unspecified", () => { + const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified", "protobufjs"); + expect(code).toMatchSnapshot(); + }); + + test("generates code for nested enum", () => { + const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum", "protobufjs"); + expect(code).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file From 74c8238d057f8e0712698da4ddaa6f332b7f7145 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sat, 7 Jun 2025 09:41:46 +0900 Subject: [PATCH 07/27] test(protoc-gen-nexus): add snapshot tests for inputObjectType printer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add tests for createInputObjectTypeCode function - Test simple inputs, nested fields, oneof fields, empty inputs - Test partial input types generation - Support google-protobuf and protobufjs libraries - Include toProto function generation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../inputObjectType.test.ts.snap | 1216 +++++++++++++++++ .../dslgen/printers/inputObjectType.test.ts | 189 +++ 2 files changed, 1405 insertions(+) create mode 100644 packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap create mode 100644 packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap new file mode 100644 index 00000000..4d25938f --- /dev/null +++ b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap @@ -0,0 +1,1216 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createInputObjectTypeCode > google-protobuf > generates code for a simple input object 1`] = ` +"import { Primitives } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; +import { inputObjectType, list, nonNull } from "nexus"; +import { stringToNumber } from "proto-nexus"; + +export const PrimitivesInput = Object.assign( + inputObjectType({ + "name": "PrimitivesInput", + "definition": (t) => { + t.field("requiredDoubleValue", { + "type": nonNull("Float"), + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }); + t.field("requiredFloatValue", { + "type": nonNull("Float"), + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }); + t.field("requiredInt32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }); + t.field("requiredInt64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }); + t.field("requiredUint32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }); + t.field("requiredUint64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }); + t.field("requiredSint32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }); + t.field("requiredSint64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }); + t.field("requiredFixed32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }); + t.field("requiredFixed64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }); + t.field("requiredSfixed32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }); + t.field("requiredSfixed64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }); + t.field("requiredBoolValue", { + "type": nonNull("Boolean"), + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }); + t.field("requiredStringValue", { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }); + t.field("requiredBytesValue", { + "type": nonNull("Byte"), + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }); + t.field("requiredDoubleValues", { + "type": nonNull(list(nonNull("Float"))), + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }); + t.field("requiredFloatValues", { + "type": nonNull(list(nonNull("Float"))), + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }); + t.field("requiredInt32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }); + t.field("requiredInt64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }); + t.field("requiredUint32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }); + t.field("requiredUint64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }); + t.field("requiredSint32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }); + t.field("requiredSint64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }); + t.field("requiredFixed32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }); + t.field("requiredFixed64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }); + t.field("requiredSfixed32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }); + t.field("requiredSfixed64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }); + t.field("requiredBoolValues", { + "type": nonNull(list(nonNull("Boolean"))), + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }); + t.field("requiredStringValues", { + "type": nonNull(list(nonNull("String"))), + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }); + t.field("requiredBytesValues", { + "type": nonNull(list(nonNull("Byte"))), + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Primitives", + "name": "Primitives", + "package": "testapis.primitives", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["PrimitivesInput"]): Primitives => { + const output = new Primitives(); + output.setRequiredDoubleValue(input.requiredDoubleValue); + output.setRequiredFloatValue(input.requiredFloatValue); + output.setRequiredInt32Value(input.requiredInt32Value); + output.setRequiredInt64Value(stringToNumber(input.requiredInt64Value)); + output.setRequiredUint32Value(input.requiredUint32Value); + output.setRequiredUint64Value(stringToNumber(input.requiredUint64Value)); + output.setRequiredSint32Value(input.requiredSint32Value); + output.setRequiredSint64Value(stringToNumber(input.requiredSint64Value)); + output.setRequiredFixed32Value(input.requiredFixed32Value); + output.setRequiredFixed64Value(stringToNumber(input.requiredFixed64Value)); + output.setRequiredSfixed32Value(input.requiredSfixed32Value); + output.setRequiredSfixed64Value(stringToNumber(input.requiredSfixed64Value)); + output.setRequiredBoolValue(input.requiredBoolValue); + output.setRequiredStringValue(input.requiredStringValue); + output.setRequiredBytesValue(input.requiredBytesValue); + output.setRequiredDoubleValuesList(input.requiredDoubleValues.map((v) => v)); + output.setRequiredFloatValuesList(input.requiredFloatValues.map((v) => v)); + output.setRequiredInt32ValuesList(input.requiredInt32Values.map((v) => v)); + output.setRequiredInt64ValuesList(input.requiredInt64Values.map((v) => stringToNumber(v))); + output.setRequiredUint32ValuesList(input.requiredUint32Values.map((v) => v)); + output.setRequiredUint64ValuesList(input.requiredUint64Values.map((v) => stringToNumber(v))); + output.setRequiredSint32ValuesList(input.requiredSint32Values.map((v) => v)); + output.setRequiredSint64ValuesList(input.requiredSint64Values.map((v) => stringToNumber(v))); + output.setRequiredFixed32ValuesList(input.requiredFixed32Values.map((v) => v)); + output.setRequiredFixed64ValuesList(input.requiredFixed64Values.map((v) => stringToNumber(v))); + output.setRequiredSfixed32ValuesList(input.requiredSfixed32Values.map((v) => v)); + output.setRequiredSfixed64ValuesList(input.requiredSfixed64Values.map((v) => stringToNumber(v))); + output.setRequiredBoolValuesList(input.requiredBoolValues.map((v) => v)); + output.setRequiredStringValuesList(input.requiredStringValues.map((v) => v)); + output.setRequiredBytesValuesList(input.requiredBytesValues.map((v) => v)); + return output; + }, + _protoNexus: { + fields: { + requiredDoubleValue: { + "type": nonNull("Float"), + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }, + requiredFloatValue: { + "type": nonNull("Float"), + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }, + requiredInt32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }, + requiredInt64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }, + requiredUint32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }, + requiredUint64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }, + requiredSint32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }, + requiredSint64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }, + requiredFixed32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }, + requiredFixed64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }, + requiredSfixed32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }, + requiredSfixed64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }, + requiredBoolValue: { + "type": nonNull("Boolean"), + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }, + requiredStringValue: { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }, + requiredBytesValue: { + "type": nonNull("Byte"), + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }, + requiredDoubleValues: { + "type": nonNull(list(nonNull("Float"))), + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }, + requiredFloatValues: { + "type": nonNull(list(nonNull("Float"))), + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }, + requiredInt32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }, + requiredInt64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }, + requiredUint32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }, + requiredUint64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }, + requiredSint32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }, + requiredSint64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }, + requiredFixed32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }, + requiredFixed64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }, + requiredSfixed32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }, + requiredSfixed64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }, + requiredBoolValues: { + "type": nonNull(list(nonNull("Boolean"))), + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }, + requiredStringValues: { + "type": nonNull(list(nonNull("String"))), + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }, + requiredBytesValues: { + "type": nonNull(list(nonNull("Byte"))), + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }, + }, + }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > google-protobuf > generates code for an input object with nested fields 1`] = ` +"import { Message } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; +import { inputObjectType, list, nonNull, nullable } from "nexus"; + +export const MessageInput = Object.assign( + inputObjectType({ + "name": "MessageInput", + "definition": (t) => { + t.field("requiredPrimitives", { + "type": nonNull("PrimitivesInput"), + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitives", { + "type": nullable("PrimitivesInput"), + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("requiredPrimitivesList", { + "type": nonNull(list(nonNull("PrimitivesInput"))), + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitivesList", { + "type": nullable(list(nonNull("PrimitivesInput"))), + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["MessageInput"]): Message => { + const output = new Message(); + output.setRequiredPrimitives(PrimitivesInput.toProto(input.requiredPrimitives)); + if (input.optionalPrimitives != null) { + output.setOptionalPrimitives(PrimitivesInput.toProto(input.optionalPrimitives)); + } + output.setRequiredPrimitivesListList(input.requiredPrimitivesList.map((v) => PrimitivesInput.toProto(v))); + if (input.optionalPrimitivesList != null) { + output.setOptionalPrimitivesListList(input.optionalPrimitivesList.map((v) => PrimitivesInput.toProto(v))); + } + return output; + }, + _protoNexus: { + fields: { + requiredPrimitives: { + "type": nonNull("PrimitivesInput"), + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + optionalPrimitives: { + "type": nullable("PrimitivesInput"), + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + requiredPrimitivesList: { + "type": nonNull(list(nonNull("PrimitivesInput"))), + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + optionalPrimitivesList: { + "type": nullable(list(nonNull("PrimitivesInput"))), + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + }, + }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > google-protobuf > generates code for an input object with oneof fields 1`] = ` +"import { OneofParent } from "@testapis/google-protobuf/testapis/oneof/oneof_pb"; +import { inputObjectType, nonNull, nullable } from "nexus"; + +export const OneofParentInput = Object.assign( + inputObjectType({ + "name": "OneofParentInput", + "definition": (t) => { + t.field("normalField", { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }); + t.field("requiredMessage1", { + "type": nullable("OneofMemberMessage1Input"), + "extensions": { + "protobufField": { "name": "required_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }); + t.field("requiredMessage2", { + "type": nullable("OneofMemberMessage2Input"), + "extensions": { + "protobufField": { "name": "required_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }); + t.field("optoinalMessage1", { + "type": nullable("OneofMemberMessage1Input"), + "extensions": { + "protobufField": { "name": "optoinal_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }); + t.field("optoinalMessage2", { + "type": nullable("OneofMemberMessage2Input"), + "extensions": { + "protobufField": { "name": "optoinal_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.oneof.OneofParent", + "name": "OneofParent", + "package": "testapis.oneof", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["OneofParentInput"]): OneofParent => { + const output = new OneofParent(); + output.setNormalField(input.normalField); + if (input.requiredMessage1 != null) { + output.setRequiredMessage1(OneofMemberMessage1Input.toProto(input.requiredMessage1)); + } + if (input.requiredMessage2 != null) { + output.setRequiredMessage2(OneofMemberMessage2Input.toProto(input.requiredMessage2)); + } + if (input.optoinalMessage1 != null) { + output.setOptoinalMessage1(OneofMemberMessage1Input.toProto(input.optoinalMessage1)); + } + if (input.optoinalMessage2 != null) { + output.setOptoinalMessage2(OneofMemberMessage2Input.toProto(input.optoinalMessage2)); + } + return output; + }, + _protoNexus: { + fields: { + normalField: { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }, + requiredMessage1: { + "type": nullable("OneofMemberMessage1Input"), + "extensions": { + "protobufField": { "name": "required_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }, + requiredMessage2: { + "type": nullable("OneofMemberMessage2Input"), + "extensions": { + "protobufField": { "name": "required_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }, + optoinalMessage1: { + "type": nullable("OneofMemberMessage1Input"), + "extensions": { + "protobufField": { "name": "optoinal_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }, + optoinalMessage2: { + "type": nullable("OneofMemberMessage2Input"), + "extensions": { + "protobufField": { "name": "optoinal_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }, + }, + }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > google-protobuf > generates code for empty input object 1`] = ` +"import { EmptyMessage } from "@testapis/google-protobuf/testapis/empty_types/empty_pb"; +import { inputObjectType } from "nexus"; + +export const EmptyMessageInput = Object.assign( + inputObjectType({ + "name": "EmptyMessageInput", + "definition": (t) => { + t.field("_", { type: "Boolean", description: "noop field" }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.empty_types.EmptyMessage", + "name": "EmptyMessage", + "package": "testapis.empty_types", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["EmptyMessageInput"]): EmptyMessage => { + const output = new EmptyMessage(); + + return output; + }, + _protoNexus: { fields: {} }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > google-protobuf > generates code for nested input types 1`] = ` +"import { ParentMessage } from "@testapis/google-protobuf/testapis/nested/nested_pb"; +import { inputObjectType, nonNull, nullable } from "nexus"; + +export const ParentMessageInput = Object.assign( + inputObjectType({ + "name": "ParentMessageInput", + "definition": (t) => { + t.field("body", { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "body", "typeFullName": "string" } }, + }); + t.field("nested", { + "type": nullable("ParentMessageNestedMessageInput"), + "extensions": { + "protobufField": { "name": "nested", "typeFullName": "testapis.nested.ParentMessage.NestedMessage" }, + }, + }); + t.field("nestedEnum", { + "type": nullable("ParentMessageNestedEnum"), + "extensions": { + "protobufField": { "name": "nested_enum", "typeFullName": "testapis.nested.ParentMessage.NestedEnum" }, + }, + }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.nested.ParentMessage", + "name": "ParentMessage", + "package": "testapis.nested", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["ParentMessageInput"]): ParentMessage => { + const output = new ParentMessage(); + output.setBody(input.body); + if (input.nested != null) { + output.setNested(ParentMessageNestedMessageInput.toProto(input.nested)); + } + if (input.nestedEnum != null) { + output.setNestedEnum(input.nestedEnum); + } + return output; + }, + _protoNexus: { + fields: { + body: { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "body", "typeFullName": "string" } }, + }, + nested: { + "type": nullable("ParentMessageNestedMessageInput"), + "extensions": { + "protobufField": { "name": "nested", "typeFullName": "testapis.nested.ParentMessage.NestedMessage" }, + }, + }, + nestedEnum: { + "type": nullable("ParentMessageNestedEnum"), + "extensions": { + "protobufField": { "name": "nested_enum", "typeFullName": "testapis.nested.ParentMessage.NestedEnum" }, + }, + }, + }, + }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > protobufjs > generates code for a simple input object 1`] = ` +"import { testapis } from "@testapis/protobufjs/testapis/primitives"; +import { inputObjectType, list, nonNull } from "nexus"; +import { stringToNumber } from "proto-nexus"; + +export const PrimitivesInput = Object.assign( + inputObjectType({ + "name": "PrimitivesInput", + "definition": (t) => { + t.field("requiredDoubleValue", { + "type": nonNull("Float"), + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }); + t.field("requiredFloatValue", { + "type": nonNull("Float"), + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }); + t.field("requiredInt32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }); + t.field("requiredInt64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }); + t.field("requiredUint32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }); + t.field("requiredUint64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }); + t.field("requiredSint32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }); + t.field("requiredSint64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }); + t.field("requiredFixed32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }); + t.field("requiredFixed64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }); + t.field("requiredSfixed32Value", { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }); + t.field("requiredSfixed64Value", { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }); + t.field("requiredBoolValue", { + "type": nonNull("Boolean"), + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }); + t.field("requiredStringValue", { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }); + t.field("requiredBytesValue", { + "type": nonNull("Byte"), + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }); + t.field("requiredDoubleValues", { + "type": nonNull(list(nonNull("Float"))), + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }); + t.field("requiredFloatValues", { + "type": nonNull(list(nonNull("Float"))), + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }); + t.field("requiredInt32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }); + t.field("requiredInt64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }); + t.field("requiredUint32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }); + t.field("requiredUint64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }); + t.field("requiredSint32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }); + t.field("requiredSint64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }); + t.field("requiredFixed32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }); + t.field("requiredFixed64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }); + t.field("requiredSfixed32Values", { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }); + t.field("requiredSfixed64Values", { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }); + t.field("requiredBoolValues", { + "type": nonNull(list(nonNull("Boolean"))), + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }); + t.field("requiredStringValues", { + "type": nonNull(list(nonNull("String"))), + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }); + t.field("requiredBytesValues", { + "type": nonNull(list(nonNull("Byte"))), + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Primitives", + "name": "Primitives", + "package": "testapis.primitives", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["PrimitivesInput"]): testapis.primitives.Primitives => { + const output = new testapis.primitives.Primitives(); + output.requiredDoubleValue = input.requiredDoubleValue; + output.requiredFloatValue = input.requiredFloatValue; + output.requiredInt32Value = input.requiredInt32Value; + output.requiredInt64Value = stringToNumber(input.requiredInt64Value); + output.requiredUint32Value = input.requiredUint32Value; + output.requiredUint64Value = stringToNumber(input.requiredUint64Value); + output.requiredSint32Value = input.requiredSint32Value; + output.requiredSint64Value = stringToNumber(input.requiredSint64Value); + output.requiredFixed32Value = input.requiredFixed32Value; + output.requiredFixed64Value = stringToNumber(input.requiredFixed64Value); + output.requiredSfixed32Value = input.requiredSfixed32Value; + output.requiredSfixed64Value = stringToNumber(input.requiredSfixed64Value); + output.requiredBoolValue = input.requiredBoolValue; + output.requiredStringValue = input.requiredStringValue; + output.requiredBytesValue = input.requiredBytesValue; + output.requiredDoubleValues = input.requiredDoubleValues.map((v) => v); + output.requiredFloatValues = input.requiredFloatValues.map((v) => v); + output.requiredInt32Values = input.requiredInt32Values.map((v) => v); + output.requiredInt64Values = input.requiredInt64Values.map((v) => stringToNumber(v)); + output.requiredUint32Values = input.requiredUint32Values.map((v) => v); + output.requiredUint64Values = input.requiredUint64Values.map((v) => stringToNumber(v)); + output.requiredSint32Values = input.requiredSint32Values.map((v) => v); + output.requiredSint64Values = input.requiredSint64Values.map((v) => stringToNumber(v)); + output.requiredFixed32Values = input.requiredFixed32Values.map((v) => v); + output.requiredFixed64Values = input.requiredFixed64Values.map((v) => stringToNumber(v)); + output.requiredSfixed32Values = input.requiredSfixed32Values.map((v) => v); + output.requiredSfixed64Values = input.requiredSfixed64Values.map((v) => stringToNumber(v)); + output.requiredBoolValues = input.requiredBoolValues.map((v) => v); + output.requiredStringValues = input.requiredStringValues.map((v) => v); + output.requiredBytesValues = input.requiredBytesValues.map((v) => v); + return output; + }, + _protoNexus: { + fields: { + requiredDoubleValue: { + "type": nonNull("Float"), + "extensions": { "protobufField": { "name": "required_double_value", "typeFullName": "double" } }, + }, + requiredFloatValue: { + "type": nonNull("Float"), + "extensions": { "protobufField": { "name": "required_float_value", "typeFullName": "float" } }, + }, + requiredInt32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_int32_value", "typeFullName": "int32" } }, + }, + requiredInt64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_int64_value", "typeFullName": "int64" } }, + }, + requiredUint32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_uint32_value", "typeFullName": "uint32" } }, + }, + requiredUint64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_uint64_value", "typeFullName": "uint64" } }, + }, + requiredSint32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_sint32_value", "typeFullName": "sint32" } }, + }, + requiredSint64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_sint64_value", "typeFullName": "sint64" } }, + }, + requiredFixed32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_fixed32_value", "typeFullName": "fixed32" } }, + }, + requiredFixed64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_fixed64_value", "typeFullName": "fixed64" } }, + }, + requiredSfixed32Value: { + "type": nonNull("Int"), + "extensions": { "protobufField": { "name": "required_sfixed32_value", "typeFullName": "sfixed32" } }, + }, + requiredSfixed64Value: { + "type": nonNull("Int64"), + "extensions": { "protobufField": { "name": "required_sfixed64_value", "typeFullName": "sfixed64" } }, + }, + requiredBoolValue: { + "type": nonNull("Boolean"), + "extensions": { "protobufField": { "name": "required_bool_value", "typeFullName": "bool" } }, + }, + requiredStringValue: { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "required_string_value", "typeFullName": "string" } }, + }, + requiredBytesValue: { + "type": nonNull("Byte"), + "extensions": { "protobufField": { "name": "required_bytes_value", "typeFullName": "bytes" } }, + }, + requiredDoubleValues: { + "type": nonNull(list(nonNull("Float"))), + "extensions": { "protobufField": { "name": "required_double_values", "typeFullName": "double" } }, + }, + requiredFloatValues: { + "type": nonNull(list(nonNull("Float"))), + "extensions": { "protobufField": { "name": "required_float_values", "typeFullName": "float" } }, + }, + requiredInt32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_int32_values", "typeFullName": "int32" } }, + }, + requiredInt64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_int64_values", "typeFullName": "int64" } }, + }, + requiredUint32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_uint32_values", "typeFullName": "uint32" } }, + }, + requiredUint64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_uint64_values", "typeFullName": "uint64" } }, + }, + requiredSint32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_sint32_values", "typeFullName": "sint32" } }, + }, + requiredSint64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_sint64_values", "typeFullName": "sint64" } }, + }, + requiredFixed32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_fixed32_values", "typeFullName": "fixed32" } }, + }, + requiredFixed64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_fixed64_values", "typeFullName": "fixed64" } }, + }, + requiredSfixed32Values: { + "type": nonNull(list(nonNull("Int"))), + "extensions": { "protobufField": { "name": "required_sfixed32_values", "typeFullName": "sfixed32" } }, + }, + requiredSfixed64Values: { + "type": nonNull(list(nonNull("Int64"))), + "extensions": { "protobufField": { "name": "required_sfixed64_values", "typeFullName": "sfixed64" } }, + }, + requiredBoolValues: { + "type": nonNull(list(nonNull("Boolean"))), + "extensions": { "protobufField": { "name": "required_bool_values", "typeFullName": "bool" } }, + }, + requiredStringValues: { + "type": nonNull(list(nonNull("String"))), + "extensions": { "protobufField": { "name": "required_string_values", "typeFullName": "string" } }, + }, + requiredBytesValues: { + "type": nonNull(list(nonNull("Byte"))), + "extensions": { "protobufField": { "name": "required_bytes_values", "typeFullName": "bytes" } }, + }, + }, + }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > protobufjs > generates code for an input object with nested fields 1`] = ` +"import { testapis } from "@testapis/protobufjs/testapis/primitives"; +import { inputObjectType, list, nonNull, nullable } from "nexus"; + +export const MessageInput = Object.assign( + inputObjectType({ + "name": "MessageInput", + "definition": (t) => { + t.field("requiredPrimitives", { + "type": nonNull("PrimitivesInput"), + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitives", { + "type": nullable("PrimitivesInput"), + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("requiredPrimitivesList", { + "type": nonNull(list(nonNull("PrimitivesInput"))), + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitivesList", { + "type": nullable(list(nonNull("PrimitivesInput"))), + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["MessageInput"]): testapis.primitives.Message => { + const output = new testapis.primitives.Message(); + output.requiredPrimitives = PrimitivesInput.toProto(input.requiredPrimitives); + if (input.optionalPrimitives != null) { + output.optionalPrimitives = PrimitivesInput.toProto(input.optionalPrimitives); + } + output.requiredPrimitivesList = input.requiredPrimitivesList.map((v) => PrimitivesInput.toProto(v)); + if (input.optionalPrimitivesList != null) { + output.optionalPrimitivesList = input.optionalPrimitivesList.map((v) => PrimitivesInput.toProto(v)); + } + return output; + }, + _protoNexus: { + fields: { + requiredPrimitives: { + "type": nonNull("PrimitivesInput"), + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + optionalPrimitives: { + "type": nullable("PrimitivesInput"), + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + requiredPrimitivesList: { + "type": nonNull(list(nonNull("PrimitivesInput"))), + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + optionalPrimitivesList: { + "type": nullable(list(nonNull("PrimitivesInput"))), + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + }, + }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > protobufjs > generates code for an input object with oneof fields 1`] = ` +"import { testapis } from "@testapis/protobufjs/testapis/oneof"; +import { inputObjectType, nonNull, nullable } from "nexus"; + +export const OneofParentInput = Object.assign( + inputObjectType({ + "name": "OneofParentInput", + "definition": (t) => { + t.field("normalField", { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }); + t.field("requiredMessage1", { + "type": nullable("OneofMemberMessage1Input"), + "extensions": { + "protobufField": { "name": "required_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }); + t.field("requiredMessage2", { + "type": nullable("OneofMemberMessage2Input"), + "extensions": { + "protobufField": { "name": "required_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }); + t.field("optoinalMessage1", { + "type": nullable("OneofMemberMessage1Input"), + "extensions": { + "protobufField": { "name": "optoinal_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }); + t.field("optoinalMessage2", { + "type": nullable("OneofMemberMessage2Input"), + "extensions": { + "protobufField": { "name": "optoinal_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.oneof.OneofParent", + "name": "OneofParent", + "package": "testapis.oneof", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["OneofParentInput"]): testapis.oneof.OneofParent => { + const output = new testapis.oneof.OneofParent(); + output.normalField = input.normalField; + if (input.requiredMessage1 != null) { + output.requiredMessage1 = OneofMemberMessage1Input.toProto(input.requiredMessage1); + } + if (input.requiredMessage2 != null) { + output.requiredMessage2 = OneofMemberMessage2Input.toProto(input.requiredMessage2); + } + if (input.optoinalMessage1 != null) { + output.optoinalMessage1 = OneofMemberMessage1Input.toProto(input.optoinalMessage1); + } + if (input.optoinalMessage2 != null) { + output.optoinalMessage2 = OneofMemberMessage2Input.toProto(input.optoinalMessage2); + } + return output; + }, + _protoNexus: { + fields: { + normalField: { + "type": nonNull("String"), + "extensions": { "protobufField": { "name": "normal_field", "typeFullName": "string" } }, + }, + requiredMessage1: { + "type": nullable("OneofMemberMessage1Input"), + "extensions": { + "protobufField": { "name": "required_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }, + requiredMessage2: { + "type": nullable("OneofMemberMessage2Input"), + "extensions": { + "protobufField": { "name": "required_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }, + optoinalMessage1: { + "type": nullable("OneofMemberMessage1Input"), + "extensions": { + "protobufField": { "name": "optoinal_message1", "typeFullName": "testapis.oneof.OneofMemberMessage1" }, + }, + }, + optoinalMessage2: { + "type": nullable("OneofMemberMessage2Input"), + "extensions": { + "protobufField": { "name": "optoinal_message2", "typeFullName": "testapis.oneof.OneofMemberMessage2" }, + }, + }, + }, + }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > protobufjs > generates code for empty input object 1`] = ` +"import { testapis } from "@testapis/protobufjs/testapis/empty_types"; +import { inputObjectType } from "nexus"; + +export const EmptyMessageInput = Object.assign( + inputObjectType({ + "name": "EmptyMessageInput", + "definition": (t) => { + t.field("_", { type: "Boolean", description: "noop field" }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.empty_types.EmptyMessage", + "name": "EmptyMessage", + "package": "testapis.empty_types", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["EmptyMessageInput"]): testapis.empty_types.EmptyMessage => { + const output = new testapis.empty_types.EmptyMessage(); + + return output; + }, + _protoNexus: { fields: {} }, + }, +); +" +`; + +exports[`createInputObjectTypeCode > with partial inputs > generates code for partial input types 1`] = ` +"import { Message } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; +import { inputObjectType, list, nonNull, nullable } from "nexus"; + +export const MessagePartialInput = Object.assign( + inputObjectType({ + "name": "MessagePartialInput", + "definition": (t) => { + t.field("requiredPrimitives", { + "type": nullable("PrimitivesPartialInput"), + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitives", { + "type": nullable("PrimitivesPartialInput"), + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("requiredPrimitivesList", { + "type": nullable(list(nonNull("PrimitivesPartialInput"))), + "description": "Required.", + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + t.field("optionalPrimitivesList", { + "type": nullable(list(nonNull("PrimitivesPartialInput"))), + "description": "Optional.", + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }); + }, + "extensions": { + "protobufMessage": { + "fullName": "testapis.primitives.Message", + "name": "Message", + "package": "testapis.primitives", + }, + }, + }), + { + toProto: (input: NexusGen["inputTypes"]["MessagePartialInput"]): Message => { + const output = new Message(); + if (input.requiredPrimitives != null) { + output.setRequiredPrimitives(PrimitivesPartialInput.toProto(input.requiredPrimitives)); + } + if (input.optionalPrimitives != null) { + output.setOptionalPrimitives(PrimitivesPartialInput.toProto(input.optionalPrimitives)); + } + if (input.requiredPrimitivesList != null) { + output.setRequiredPrimitivesListList( + input.requiredPrimitivesList.map((v) => PrimitivesPartialInput.toProto(v)), + ); + } + if (input.optionalPrimitivesList != null) { + output.setOptionalPrimitivesListList( + input.optionalPrimitivesList.map((v) => PrimitivesPartialInput.toProto(v)), + ); + } + return output; + }, + _protoNexus: { + fields: { + requiredPrimitives: { + "type": nullable("PrimitivesPartialInput"), + "extensions": { + "protobufField": { "name": "required_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + optionalPrimitives: { + "type": nullable("PrimitivesPartialInput"), + "extensions": { + "protobufField": { "name": "optional_primitives", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + requiredPrimitivesList: { + "type": nullable(list(nonNull("PrimitivesPartialInput"))), + "extensions": { + "protobufField": { "name": "required_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + optionalPrimitivesList: { + "type": nullable(list(nonNull("PrimitivesPartialInput"))), + "extensions": { + "protobufField": { "name": "optional_primitives_list", "typeFullName": "testapis.primitives.Primitives" }, + }, + }, + }, + }, + }, +); +" +`; diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts new file mode 100644 index 00000000..27f82bc1 --- /dev/null +++ b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts @@ -0,0 +1,189 @@ +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; +import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { + InputObjectType, + collectTypesFromFile, + createRegistryFromSchema, + defaultScalarMapping +} from "@proto-graphql/codegen-core"; +import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { code } from "ts-poet"; +import { describe, expect, test } from "vitest"; +import { createInputObjectTypeCode } from "./inputObjectType.js"; +import type { NexusPrinterOptions } from "./util.js"; + +// Helper to extract InputObjectType instances and generate code +function generateInputObjectTypeCode(packageName: string, typeName: string, options: NexusPrinterOptions) { + const req = buildCodeGeneratorRequest(packageName); + + // Default type options + const typeOptions = { + partialInputs: false, + scalarMapping: defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + // Create a minimal plugin to get Schema + const plugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: createTsGenerator({ + generateFiles: (schema, file) => { + // Just return, we only need the schema + }, + dsl: "nexus", + }), + parseOptions: parseNexusOptions, + }); + + // Run the plugin to get the transformed schema + const resp = plugin.run(req); + + // Now we need to recreate the schema to access its internals + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + // Find the target file + const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); + if (!targetFile) throw new Error(`File for ${packageName} not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const inputType = types.find(t => t.typeName === typeName && t instanceof InputObjectType) as InputObjectType; + if (!inputType) throw new Error(`${typeName} type not found`); + + const generatedCode = createInputObjectTypeCode(inputType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parseNexusOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + return result; +} + +describe("createInputObjectTypeCode", () => { + describe("google-protobuf", () => { + const options: NexusPrinterOptions = { + dsl: "nexus", + protobuf: "google-protobuf" as const, + importPrefix: "@testapis/google-protobuf", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".nexus", + }; + + test("generates code for a simple input object", () => { + const code = generateInputObjectTypeCode("testapis.primitives", "PrimitivesInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an input object with nested fields", () => { + const code = generateInputObjectTypeCode("testapis.primitives", "MessageInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an input object with oneof fields", () => { + const code = generateInputObjectTypeCode("testapis.oneof", "OneofParentInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for empty input object", () => { + const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessageInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for nested input types", () => { + const code = generateInputObjectTypeCode("testapis.nested", "ParentMessageInput", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("protobufjs", () => { + const options: NexusPrinterOptions = { + dsl: "nexus", + protobuf: "protobufjs" as const, + importPrefix: "@testapis/protobufjs", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".nexus", + }; + + test("generates code for a simple input object", () => { + const code = generateInputObjectTypeCode("testapis.primitives", "PrimitivesInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an input object with nested fields", () => { + const code = generateInputObjectTypeCode("testapis.primitives", "MessageInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for an input object with oneof fields", () => { + const code = generateInputObjectTypeCode("testapis.oneof", "OneofParentInput", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for empty input object", () => { + const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessageInput", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("with partial inputs", () => { + const options: NexusPrinterOptions = { + dsl: "nexus", + protobuf: "google-protobuf" as const, + importPrefix: "@testapis/google-protobuf", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".nexus", + }; + + test("generates code for partial input types", () => { + const req = buildCodeGeneratorRequest("testapis.primitives"); + + // Create type options with partialInputs enabled + const typeOptions = { + partialInputs: true, + scalarMapping: defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + const targetFile = schema.allFiles.find(f => f.name.includes("primitives")); + if (!targetFile) throw new Error(`File for primitives not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const partialInputType = types.find(t => t.typeName === "MessagePartialInput" && t instanceof InputObjectType) as InputObjectType; + if (!partialInputType) throw new Error(`MessagePartialInput type not found`); + + const generatedCode = createInputObjectTypeCode(partialInputType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parseNexusOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + + expect(result).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file From 7224f84421d5020d9a59b5ae5823acd4a77607ba Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sat, 7 Jun 2025 09:47:03 +0900 Subject: [PATCH 08/27] test(protoc-gen-nexus): add tests for oneofUnionType printer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add comprehensive tests for oneofUnionType code generation - Test simple oneof unions (required and optional) - Test squashed unions from import_squashed_union edge case - Cover both google-protobuf and protobufjs libraries - Generate snapshots for expected output validation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 87 +++++++++++ package.json | 2 +- .../__snapshots__/oneofUnionType.test.ts.snap | 147 ++++++++++++++++++ .../dslgen/printers/oneofUnionType.test.ts | 131 ++++++++++++++++ 4 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 CLAUDE.md create mode 100644 packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap create mode 100644 packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..9c028a71 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,87 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is proto-graphql-js, a Protobuf-First GraphQL Schema generator for JavaScript/TypeScript. It provides protoc plugins that generate GraphQL schema code from Protocol Buffer definitions. + +### Supported GraphQL Libraries +- **Pothos**: via `protoc-gen-pothos` package +- **Nexus**: via `protoc-gen-nexus` package + +### Supported Protobuf Libraries +- google-protobuf +- protobufjs +- ts-proto +- @bufbuild/protobuf (protobuf-es) + +## Essential Commands + +```bash +# Install dependencies +pnpm install + +# Build all packages +pnpm build + +# Run unit tests +pnpm test + +# Run E2E tests (generates test APIs first) +pnpm test:e2e + +# Lint and format code (auto-fix) +pnpm lint + +# Clean build artifacts +pnpm clean + +# Run a specific test file +pnpm vitest path/to/test.spec.ts + +# Run tests in watch mode +pnpm vitest --watch +``` + +## Repository Structure + +This is a pnpm workspace monorepo with packages organized as: +- `/packages/` - Main packages (protoc-gen-pothos, protoc-gen-nexus, etc.) +- `/devPackages/` - Development utilities and test proto files +- `/e2e/` - End-to-end test suites with generated GraphQL schemas + +Build orchestration uses Turbo for caching and parallel execution. + +## Code Style and Conventions + +- **ESM-first** with CommonJS compatibility (type: "module" in package.json) +- **TypeScript** with strict typing +- **Biome** for linting and formatting +- **No default exports** (enforced by linter) +- **Organized imports** (auto-organized by Biome) +- **2-space indentation** + +## Testing Strategy + +- Unit tests use Vitest +- E2E tests generate GraphQL schemas from test proto files and verify output +- Test matrix covers combinations of GraphQL libraries × Protobuf implementations +- Snapshot testing for generated schema verification + +## Proto Compilation Workflow + +The project generates GraphQL schema code from .proto files: + +1. Proto files → protoc with custom plugin → GraphQL schema code +2. Plugins read proto descriptors and generate type-safe GraphQL definitions +3. Generated code integrates with Pothos or Nexus GraphQL libraries + +Key proto extensions are defined in `/proto-graphql/proto/graphql/schema.proto`. + +## Development Tips + +- When modifying protoc plugins, rebuild before testing: `pnpm build` +- E2E tests require proto compilation: `pnpm test:e2e:gen` before `pnpm test:e2e` +- Use `pnpm lint` to auto-fix formatting issues +- Generated files are in `__generated__` directories (ignored by linter) \ No newline at end of file diff --git a/package.json b/package.json index 3ecbf30f..9a6dc4ff 100644 --- a/package.json +++ b/package.json @@ -45,5 +45,5 @@ } }, "name": "proto-nexus", - "packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b" + "packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677" } diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap new file mode 100644 index 00000000..b9069495 --- /dev/null +++ b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap @@ -0,0 +1,147 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`createOneofUnionTypeCode > google-protobuf > generates code for a simple oneof union 1`] = ` +"import { unionType } from "nexus"; + +export const OneofParentRequiredOneofMembers = unionType({ + "name": "OneofParentRequiredOneofMembers", + "description": "Required. disallow not_set.", + "definition": (t) => { + t.members(OneofMemberMessage1, OneofMemberMessage2); + }, + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.required_oneof_members", + "name": "required_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "required_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "required_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > google-protobuf > generates code for import squashed union 1`] = ` +"import { unionType } from "nexus"; + +export const SquashedOneof = unionType({ + "name": "SquashedOneof", + "definition": (t) => { + t.members(OneofMessage1); + }, + "extensions": { + "protobufOneof": { + "fullName": "testapis.edgecases.import_squashed_union.pkg1.SquashedOneof", + "name": "SquashedOneof", + "package": "testapis.edgecases.import_squashed_union.pkg1", + "fields": [{ + "name": "msg1", + "type": "testapis.edgecases.import_squashed_union.pkg1.OneofMessage1", + "options": { "[graphql.object_type]": { "squashUnion": true } }, + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > google-protobuf > generates code for optional oneof union 1`] = ` +"import { unionType } from "nexus"; + +export const OneofParentOptionalOneofMembers = unionType({ + "name": "OneofParentOptionalOneofMembers", + "definition": (t) => { + t.members(OneofMemberMessage1, OneofMemberMessage2); + }, + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.optional_oneof_members", + "name": "optional_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "optoinal_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "optoinal_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > protobufjs > generates code for a simple oneof union 1`] = ` +"import { unionType } from "nexus"; + +export const OneofParentRequiredOneofMembers = unionType({ + "name": "OneofParentRequiredOneofMembers", + "description": "Required. disallow not_set.", + "definition": (t) => { + t.members(OneofMemberMessage1, OneofMemberMessage2); + }, + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.required_oneof_members", + "name": "required_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "required_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "required_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > protobufjs > generates code for import squashed union 1`] = ` +"import { unionType } from "nexus"; + +export const SquashedOneof = unionType({ + "name": "SquashedOneof", + "definition": (t) => { + t.members(OneofMessage1); + }, + "extensions": { + "protobufOneof": { + "fullName": "testapis.edgecases.import_squashed_union.pkg1.SquashedOneof", + "name": "SquashedOneof", + "package": "testapis.edgecases.import_squashed_union.pkg1", + "fields": [{ + "name": "msg1", + "type": "testapis.edgecases.import_squashed_union.pkg1.OneofMessage1", + "options": { "[graphql.object_type]": { "squashUnion": true } }, + }], + }, + }, +}); +" +`; + +exports[`createOneofUnionTypeCode > protobufjs > generates code for optional oneof union 1`] = ` +"import { unionType } from "nexus"; + +export const OneofParentOptionalOneofMembers = unionType({ + "name": "OneofParentOptionalOneofMembers", + "definition": (t) => { + t.members(OneofMemberMessage1, OneofMemberMessage2); + }, + "extensions": { + "protobufOneof": { + "fullName": "testapis.oneof.OneofParent.optional_oneof_members", + "name": "optional_oneof_members", + "messageName": "OneofParent", + "package": "testapis.oneof", + "fields": [{ "name": "optoinal_message1", "type": "testapis.oneof.OneofMemberMessage1" }, { + "name": "optoinal_message2", + "type": "testapis.oneof.OneofMemberMessage2", + }], + }, + }, +}); +" +`; diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts new file mode 100644 index 00000000..e1e0b9aa --- /dev/null +++ b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts @@ -0,0 +1,131 @@ +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; +import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { + OneofUnionType, + SquashedOneofUnionType, + collectTypesFromFile, + createRegistryFromSchema, + defaultScalarMapping +} from "@proto-graphql/codegen-core"; +import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { code } from "ts-poet"; +import { describe, expect, test } from "vitest"; +import { createOneofUnionTypeCode } from "./oneofUnionType.js"; +import type { NexusPrinterOptions } from "./util.js"; + +// Helper to extract OneofUnionType instances and generate code +function generateOneofUnionTypeCode(packageName: string, typeName: string, options: NexusPrinterOptions) { + const req = buildCodeGeneratorRequest(packageName); + + // Default type options + const typeOptions = { + partialInputs: false, + scalarMapping: defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + // Create a minimal plugin to get Schema + const plugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: createTsGenerator({ + generateFiles: (schema, file) => { + // Just return, we only need the schema + }, + dsl: "nexus", + }), + parseOptions: parseNexusOptions, + }); + + // Run the plugin to get the transformed schema + const resp = plugin.run(req); + + // Now we need to recreate the schema to access its internals + const testPlugin = createEcmaScriptPlugin({ + name: "test-plugin", + version: "v1.0.0", + generateTs: (schema) => { + const registry = createRegistryFromSchema(schema); + + // Find the target file + let targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); + + // Special handling for edge cases + if (packageName === "testapis.edgecases.import_squashed_union.pkg1") { + targetFile = schema.allFiles.find(f => f.name.includes("import_squashed_union/pkg1/types")); + } + + if (!targetFile) throw new Error(`File for ${packageName} not found`); + + const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const oneofType = types.find(t => t.typeName === typeName && (t instanceof OneofUnionType || t instanceof SquashedOneofUnionType)) as OneofUnionType | SquashedOneofUnionType; + if (!oneofType) throw new Error(`${typeName} type not found`); + + const generatedCode = createOneofUnionTypeCode(oneofType, registry, options); + + // Store the result for testing + (global as any).testResult = generatedCode.toString(); + }, + parseOptions: parseNexusOptions, + }); + + testPlugin.run(req); + + const result = (global as any).testResult; + delete (global as any).testResult; + return result; +} + +describe("createOneofUnionTypeCode", () => { + describe("google-protobuf", () => { + const options: NexusPrinterOptions = { + dsl: "nexus", + protobuf: "google-protobuf" as const, + importPrefix: "@testapis/google-protobuf", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".nexus", + }; + + test("generates code for a simple oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for optional oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentOptionalOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for import squashed union", () => { + const code = generateOneofUnionTypeCode("testapis.edgecases.import_squashed_union.pkg1", "SquashedOneof", options); + expect(code).toMatchSnapshot(); + }); + }); + + describe("protobufjs", () => { + const options: NexusPrinterOptions = { + dsl: "nexus", + protobuf: "protobufjs" as const, + importPrefix: "@testapis/protobufjs", + emitImportedFiles: false, + fileLayout: "proto_file", + filenameSuffix: ".nexus", + }; + + test("generates code for a simple oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for optional oneof union", () => { + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentOptionalOneofMembers", options); + expect(code).toMatchSnapshot(); + }); + + test("generates code for import squashed union", () => { + const code = generateOneofUnionTypeCode("testapis.edgecases.import_squashed_union.pkg1", "SquashedOneof", options); + expect(code).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file From f84fd8ef67c807796b94a0f46fa1fdb3e1c5d565 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 10:47:21 +0900 Subject: [PATCH 09/27] refactor(protoc-gen-pothos): simplify objectType test to use direct registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use getTestapisFileDescriptorSet and createFileRegistry directly - Create ObjectType directly from descriptor message - Remove plugin wrapper and global variable hack - Change packageName type to TestapisPackage for type safety - Fix test case to use correct message name 'PrefixedMessage' 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/dslgen/printers/objectType.test.ts | 80 ++++++------------- 1 file changed, 23 insertions(+), 57 deletions(-) diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts index 0a9250c3..db871bb6 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts @@ -1,73 +1,39 @@ import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; -import { - ObjectType, +import { buildCodeGeneratorRequest, getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; +import { + ObjectType, collectTypesFromFile, createRegistryFromSchema, defaultScalarMappingForTsProto, - defaultScalarMapping + defaultScalarMapping, + TypeOptions } from "@proto-graphql/codegen-core"; import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; import { code } from "ts-poet"; import { describe, expect, test } from "vitest"; import { createObjectTypeCode } from "./objectType.js"; import type { PothosPrinterOptions } from "./util.js"; +import { createFileRegistry, createRegistry } from "@bufbuild/protobuf"; -// Helper to extract ObjectType instances and generate code -function generateObjectTypeCode(packageName: string, messageTypeName: string, options: PothosPrinterOptions) { - const req = buildCodeGeneratorRequest(packageName); - - // Default type options - const typeOptions = { +function generateObjectTypeCode(packageName: TestapisPackage, messageTypeName: string, options: PothosPrinterOptions): string { + const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; - - // Create a minimal plugin to get Schema - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: createTsGenerator({ - generateFiles: (schema, file) => { - // Just return, we only need the schema - }, - dsl: "pothos", - }), - parseOptions: parsePothosOptions, - }); - - // Run the plugin to get the transformed schema - const resp = plugin.run(req); - - // Now we need to recreate the schema to access its internals - const testPlugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - // Find the target file - const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); - if (!targetFile) throw new Error(`File for ${packageName} not found`); - - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - const objectType = types.find(t => t.typeName === messageTypeName && t instanceof ObjectType) as ObjectType; - if (!objectType) throw new Error(`${messageTypeName} type not found`); - - const generatedCode = createObjectTypeCode(objectType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); - }, - parseOptions: parsePothosOptions, - }); - - testPlugin.run(req); - - const result = (global as any).testResult; - delete (global as any).testResult; - return result; + + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet) + const descMsg = registry.getMessage(`${packageName}.${messageTypeName}`); + if (descMsg === undefined) { + throw new Error(`Message ${messageTypeName} not found in package ${packageName}`); + } + + const objType = new ObjectType(descMsg!, typeOptions) + + const code = createObjectTypeCode(objType, registry, options) + + return code.toString() } describe("createObjectTypeCode", () => { @@ -153,8 +119,8 @@ describe("createObjectTypeCode", () => { }; test("generates code for message with field extensions", () => { - const code = generateObjectTypeCode("testapis.extensions", "TestPrefixPrefixedMessage", options); + const code = generateObjectTypeCode("testapis.extensions", "PrefixedMessage", options); expect(code).toMatchSnapshot(); }); }); -}); \ No newline at end of file +}); From 2b64ef77cb266356d31647694155444c8007f9e7 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 10:51:29 +0900 Subject: [PATCH 10/27] refactor(protoc-gen-pothos): simplify enumType test to use direct registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use getTestapisFileDescriptorSet and createFileRegistry directly - Create EnumType directly from descriptor enum - Remove plugin wrapper and global variable hack - Change packageName type to TestapisPackage for type safety - Add special handling for nested enums and package name differences - Fix test case to use correct enum name 'PrefixedEnum' 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/dslgen/printers/enumType.test.ts | 87 +++++++------------ 1 file changed, 31 insertions(+), 56 deletions(-) diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts index 877a907d..02aee645 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts @@ -1,73 +1,48 @@ -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; import { - EnumType, - collectTypesFromFile, - createRegistryFromSchema, + EnumType, + TypeOptions, defaultScalarMappingForTsProto, defaultScalarMapping } from "@proto-graphql/codegen-core"; -import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; -import { code } from "ts-poet"; +import { createFileRegistry } from "@bufbuild/protobuf"; import { describe, expect, test } from "vitest"; import { createEnumTypeCode } from "./enumType.js"; import type { PothosPrinterOptions } from "./util.js"; -// Helper to extract EnumType instances and generate code -function generateEnumTypeCode(packageName: string, enumTypeName: string, options: PothosPrinterOptions) { - const req = buildCodeGeneratorRequest(packageName); - - // Default type options - const typeOptions = { +function generateEnumTypeCode(packageName: TestapisPackage, enumTypeName: string, options: PothosPrinterOptions): string { + const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; + + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet); + // For nested enums, we need to check the parent message + let descEnum = registry.getEnum(`${packageName}.${enumTypeName}`); - // Create a minimal plugin to get Schema - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: createTsGenerator({ - generateFiles: (schema, file) => { - // Just return, we only need the schema - }, - dsl: "pothos", - }), - parseOptions: parsePothosOptions, - }); - - // Run the plugin to get the transformed schema - const resp = plugin.run(req); - - // Now we need to recreate the schema to access its internals - const testPlugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - // Find the target file - const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); - if (!targetFile) throw new Error(`File for ${packageName} not found`); - - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - const enumType = types.find(t => t.typeName === enumTypeName && t instanceof EnumType) as EnumType; - if (!enumType) throw new Error(`${enumTypeName} type not found`); - - const generatedCode = createEnumTypeCode(enumType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); - }, - parseOptions: parsePothosOptions, - }); + // If not found directly, it might be a nested enum + if (descEnum === undefined && enumTypeName.includes("NestedEnum")) { + const parentMessageName = enumTypeName.replace("NestedEnum", ""); + descEnum = registry.getEnum(`${packageName}.${parentMessageName}.NestedEnum`); + } - testPlugin.run(req); + if (descEnum === undefined) { + // Try with the actual proto package name (testapi instead of testapis) + const actualPackageName = packageName.replace("testapis", "testapi"); + descEnum = registry.getEnum(`${actualPackageName}.${enumTypeName}`); + } - const result = (global as any).testResult; - delete (global as any).testResult; - return result; + if (descEnum === undefined) { + throw new Error(`Enum ${enumTypeName} not found in package ${packageName}`); + } + + const enumType = new EnumType(descEnum, typeOptions); + + const code = createEnumTypeCode(enumType, registry, options); + + return code.toString(); } describe("createEnumTypeCode", () => { @@ -100,7 +75,7 @@ describe("createEnumTypeCode", () => { }); test("generates code for enum with extensions", () => { - const code = generateEnumTypeCode("testapis.extensions", "TestPrefixPrefixedEnum", options); + const code = generateEnumTypeCode("testapis.extensions", "PrefixedEnum", options); expect(code).toMatchSnapshot(); }); }); From efad7a635992ad72ee31915baa8f5fd132a6cb62 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 10:57:05 +0900 Subject: [PATCH 11/27] refactor(protoc-gen-pothos): update inputObjectType test to use plugin approach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use buildCodeGeneratorRequest and plugin to get proper Schema context - Remove direct registry usage as InputObjectType requires Schema context - Change packageName type to TestapisPackage for type safety - Add partialInputs parameter to support partial input testing - Simplify partial input test case Note: This is a temporary solution. A better approach would be to construct InputObjectType directly from descriptors, but that requires more investigation into the Schema/File context requirements. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../dslgen/printers/inputObjectType.test.ts | 99 +++++-------------- 1 file changed, 23 insertions(+), 76 deletions(-) diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts index 4f5def1e..dd9f1d43 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts @@ -1,47 +1,32 @@ -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { getTestapisFileDescriptorSet, TestapisPackage, buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; import { - InputObjectType, - collectTypesFromFile, - createRegistryFromSchema, + InputObjectType, + ObjectType, + TypeOptions, defaultScalarMappingForTsProto, - defaultScalarMapping + defaultScalarMapping, + collectTypesFromFile, + createRegistryFromSchema } from "@proto-graphql/codegen-core"; +import { createFileRegistry } from "@bufbuild/protobuf"; +import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; -import { code } from "ts-poet"; import { describe, expect, test } from "vitest"; import { createInputObjectTypeCode } from "./inputObjectType.js"; import type { PothosPrinterOptions } from "./util.js"; -// Helper to extract InputObjectType instances and generate code -function generateInputObjectTypeCode(packageName: string, typeName: string, options: PothosPrinterOptions) { - const req = buildCodeGeneratorRequest(packageName); - - // Default type options - const typeOptions = { - partialInputs: false, +function generateInputObjectTypeCode(packageName: TestapisPackage, typeName: string, options: PothosPrinterOptions, partialInputs = false): string { + const typeOptions: TypeOptions = { + partialInputs, scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; + + const req = buildCodeGeneratorRequest(packageName); - // Create a minimal plugin to get Schema + // We need to run through the plugin to get a proper Schema + let result = ""; const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: createTsGenerator({ - generateFiles: (schema, file) => { - // Just return, we only need the schema - }, - dsl: "pothos", - }), - parseOptions: parsePothosOptions, - }); - - // Run the plugin to get the transformed schema - const resp = plugin.run(req); - - // Now we need to recreate the schema to access its internals - const testPlugin = createEcmaScriptPlugin({ name: "test-plugin", version: "v1.0.0", generateTs: (schema) => { @@ -51,22 +36,18 @@ function generateInputObjectTypeCode(packageName: string, typeName: string, opti const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); if (!targetFile) throw new Error(`File for ${packageName} not found`); + // Collect types from the file const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const inputType = types.find(t => t.typeName === typeName && t instanceof InputObjectType) as InputObjectType; if (!inputType) throw new Error(`${typeName} type not found`); - - const generatedCode = createInputObjectTypeCode(inputType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); + + result = createInputObjectTypeCode(inputType, registry, options).toString(); }, parseOptions: parsePothosOptions, }); - testPlugin.run(req); - - const result = (global as any).testResult; - delete (global as any).testResult; + plugin.run(req); return result; } @@ -158,42 +139,8 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for partial input types", () => { - const req = buildCodeGeneratorRequest("testapis.primitives"); - - // Create type options with partialInputs enabled - const typeOptions = { - partialInputs: true, - scalarMapping: defaultScalarMappingForTsProto, - ignoreNonMessageOneofFields: false, - }; - - const testPlugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - const targetFile = schema.allFiles.find(f => f.name.includes("primitives")); - if (!targetFile) throw new Error(`File for primitives not found`); - - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - const partialInputType = types.find(t => t.typeName === "MessagePartialInput" && t instanceof InputObjectType) as InputObjectType; - if (!partialInputType) throw new Error(`MessagePartialInput type not found`); - - const generatedCode = createInputObjectTypeCode(partialInputType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); - }, - parseOptions: parsePothosOptions, - }); - - testPlugin.run(req); - - const result = (global as any).testResult; - delete (global as any).testResult; - - expect(result).toMatchSnapshot(); + const code = generateInputObjectTypeCode("testapis.primitives", "MessagePartialInput", options, true); + expect(code).toMatchSnapshot(); }); }); }); \ No newline at end of file From 68ade18dc55effc1a335a240c3c0c4e1417454b3 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 11:01:01 +0900 Subject: [PATCH 12/27] refactor(protoc-gen-pothos): simplify oneofUnionType test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use buildCodeGeneratorRequest and createEcmaScriptPlugin - Change packageName type to TestapisPackage for type safety - Remove unnecessary plugin wrapper and global variable hack - Keep special handling for import_oneof_member_from_other_file edge case 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../dslgen/printers/oneofUnionType.test.ts | 42 ++++--------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts index f734c0d9..1c6fd0c0 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts @@ -1,48 +1,30 @@ import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { buildCodeGeneratorRequest, TestapisPackage } from "@proto-graphql/testapis-proto"; import { OneofUnionType, SquashedOneofUnionType, + TypeOptions, collectTypesFromFile, createRegistryFromSchema, defaultScalarMappingForTsProto, defaultScalarMapping } from "@proto-graphql/codegen-core"; import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; -import { code } from "ts-poet"; import { describe, expect, test } from "vitest"; import { createOneofUnionTypeCode } from "./oneofUnionType.js"; import type { PothosPrinterOptions } from "./util.js"; -// Helper to extract OneofUnionType instances and generate code -function generateOneofUnionTypeCode(packageName: string, typeName: string, options: PothosPrinterOptions) { - const req = buildCodeGeneratorRequest(packageName); - - // Default type options - const typeOptions = { +function generateOneofUnionTypeCode(packageName: TestapisPackage, typeName: string, options: PothosPrinterOptions): string { + const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; - // Create a minimal plugin to get Schema - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: createTsGenerator({ - generateFiles: (schema, file) => { - // Just return, we only need the schema - }, - dsl: "pothos", - }), - parseOptions: parsePothosOptions, - }); - - // Run the plugin to get the transformed schema - const resp = plugin.run(req); + const req = buildCodeGeneratorRequest(packageName); - // Now we need to recreate the schema to access its internals - const testPlugin = createEcmaScriptPlugin({ + let result = ""; + const plugin = createEcmaScriptPlugin({ name: "test-plugin", version: "v1.0.0", generateTs: (schema) => { @@ -64,18 +46,12 @@ function generateOneofUnionTypeCode(packageName: string, typeName: string, optio const oneofType = types.find(t => t.typeName === typeName && (t instanceof OneofUnionType || t instanceof SquashedOneofUnionType)); if (!oneofType) throw new Error(`${typeName} type not found`); - const generatedCode = createOneofUnionTypeCode(oneofType as OneofUnionType | SquashedOneofUnionType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); + result = createOneofUnionTypeCode(oneofType as OneofUnionType | SquashedOneofUnionType, registry, options).toString(); }, parseOptions: parsePothosOptions, }); - testPlugin.run(req); - - const result = (global as any).testResult; - delete (global as any).testResult; + plugin.run(req); return result; } From 740f8cf5229829a13922185684e5d8ae8bd73d77 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 11:02:36 +0900 Subject: [PATCH 13/27] refactor(protoc-gen-nexus): simplify objectType test to use direct registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use getTestapisFileDescriptorSet and createFileRegistry directly - Create ObjectType directly from descriptor message - Remove plugin wrapper and global variable hack - Change packageName type to TestapisPackage for type safety - Fix test case to use correct message name 'PrefixedMessage' - Remove ts-proto support as nexus doesn't support it 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/dslgen/printers/objectType.test.ts | 82 +++++-------------- 1 file changed, 21 insertions(+), 61 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts index 9f6c54b5..ec879ebc 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts @@ -1,73 +1,33 @@ -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; import { - ObjectType, - collectTypesFromFile, - createRegistryFromSchema, - defaultScalarMappingForTsProto, + ObjectType, + TypeOptions, defaultScalarMapping } from "@proto-graphql/codegen-core"; -import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; -import { code } from "ts-poet"; +import { createFileRegistry } from "@bufbuild/protobuf"; import { describe, expect, test } from "vitest"; import { createObjectTypeCode } from "./objectType.js"; import type { NexusPrinterOptions } from "./util.js"; -// Helper to extract ObjectType instances and generate code -function generateObjectTypeCode(packageName: string, messageTypeName: string, options: NexusPrinterOptions) { - const req = buildCodeGeneratorRequest(packageName); - - // Default type options - const typeOptions = { +function generateObjectTypeCode(packageName: TestapisPackage, messageTypeName: string, options: NexusPrinterOptions): string { + const typeOptions: TypeOptions = { partialInputs: false, - scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + scalarMapping: defaultScalarMapping, ignoreNonMessageOneofFields: false, }; - - // Create a minimal plugin to get Schema - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: createTsGenerator({ - generateFiles: (schema, file) => { - // Just return, we only need the schema - }, - dsl: "nexus", - }), - parseOptions: parseNexusOptions, - }); - - // Run the plugin to get the transformed schema - const resp = plugin.run(req); - - // Now we need to recreate the schema to access its internals - const testPlugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - // Find the target file - const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); - if (!targetFile) throw new Error(`File for ${packageName} not found`); - - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - const objectType = types.find(t => t.typeName === messageTypeName && t instanceof ObjectType) as ObjectType; - if (!objectType) throw new Error(`${messageTypeName} type not found`); - - const generatedCode = createObjectTypeCode(objectType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); - }, - parseOptions: parseNexusOptions, - }); - - testPlugin.run(req); - - const result = (global as any).testResult; - delete (global as any).testResult; - return result; + + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet); + const descMsg = registry.getMessage(`${packageName}.${messageTypeName}`); + if (descMsg === undefined) { + throw new Error(`Message ${messageTypeName} not found in package ${packageName}`); + } + + const objType = new ObjectType(descMsg, typeOptions); + + const code = createObjectTypeCode(objType, registry, options); + + return code.toString(); } describe("createObjectTypeCode", () => { @@ -129,7 +89,7 @@ describe("createObjectTypeCode", () => { }; test("generates code for message with field extensions", () => { - const code = generateObjectTypeCode("testapis.extensions", "TestPrefixPrefixedMessage", options); + const code = generateObjectTypeCode("testapis.extensions", "PrefixedMessage", options); expect(code).toMatchSnapshot(); }); }); From c956827671962ce1fab40400e87324373860a4ea Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 11:04:15 +0900 Subject: [PATCH 14/27] refactor(protoc-gen-nexus): simplify enumType test to use direct registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use getTestapisFileDescriptorSet and createFileRegistry directly - Create EnumType directly from descriptor enum - Remove plugin wrapper and global variable hack - Change packageName type to TestapisPackage for type safety - Add special handling for nested enums and package name differences - Fix test case to use correct enum name 'PrefixedEnum' - Remove protobuf parameter from helper function 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/dslgen/printers/enumType.test.ts | 99 +++++++------------ 1 file changed, 37 insertions(+), 62 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts index 1ec33c98..c552fe76 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts @@ -1,110 +1,85 @@ -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; import { - EnumType, - collectTypesFromFile, - createRegistryFromSchema, - defaultScalarMappingForTsProto, + EnumType, + TypeOptions, defaultScalarMapping } from "@proto-graphql/codegen-core"; -import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; -import { code } from "ts-poet"; +import { createFileRegistry } from "@bufbuild/protobuf"; import { describe, expect, test } from "vitest"; import { createEnumTypeCode } from "./enumType.js"; -// Helper to extract EnumType instances and generate code -function generateEnumTypeCode(packageName: string, enumTypeName: string, protobuf: "google-protobuf" | "protobufjs") { - const req = buildCodeGeneratorRequest(packageName); - - // Default type options - const typeOptions = { +function generateEnumTypeCode(packageName: TestapisPackage, enumTypeName: string): string { + const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: defaultScalarMapping, ignoreNonMessageOneofFields: false, }; + + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet); - // Create a minimal plugin to get Schema - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: createTsGenerator({ - generateFiles: (schema, file) => { - // Just return, we only need the schema - }, - dsl: "nexus", - }), - parseOptions: parseNexusOptions, - }); - - // Run the plugin to get the transformed schema - const resp = plugin.run(req); + // For nested enums, we need to check the parent message + let descEnum = registry.getEnum(`${packageName}.${enumTypeName}`); - // Now we need to recreate the schema to access its internals - const testPlugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - // Find the target file - const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); - if (!targetFile) throw new Error(`File for ${packageName} not found`); - - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - const enumType = types.find(t => t.typeName === enumTypeName && t instanceof EnumType) as EnumType; - if (!enumType) throw new Error(`${enumTypeName} type not found`); - - const generatedCode = createEnumTypeCode(enumType, registry); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); - }, - parseOptions: parseNexusOptions, - }); + // If not found directly, it might be a nested enum + if (descEnum === undefined && enumTypeName.includes("NestedEnum")) { + const parentMessageName = enumTypeName.replace("NestedEnum", ""); + descEnum = registry.getEnum(`${packageName}.${parentMessageName}.NestedEnum`); + } - testPlugin.run(req); + if (descEnum === undefined) { + // Try with the actual proto package name (testapi instead of testapis) + const actualPackageName = packageName.replace("testapis", "testapi"); + descEnum = registry.getEnum(`${actualPackageName}.${enumTypeName}`); + } - const result = (global as any).testResult; - delete (global as any).testResult; - return result; + if (descEnum === undefined) { + throw new Error(`Enum ${enumTypeName} not found in package ${packageName}`); + } + + const enumType = new EnumType(descEnum, typeOptions); + + const code = createEnumTypeCode(enumType, registry); + + return code.toString(); } describe("createEnumTypeCode", () => { describe("google-protobuf", () => { test("generates code for a simple enum", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnum", "google-protobuf"); + const code = generateEnumTypeCode("testapis.enums", "MyEnum"); expect(code).toMatchSnapshot(); }); test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified", "google-protobuf"); + const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified"); expect(code).toMatchSnapshot(); }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum", "google-protobuf"); + const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum"); expect(code).toMatchSnapshot(); }); test("generates code for enum with extensions", () => { - const code = generateEnumTypeCode("testapis.extensions", "TestPrefixPrefixedEnum", "google-protobuf"); + const code = generateEnumTypeCode("testapis.extensions", "PrefixedEnum"); expect(code).toMatchSnapshot(); }); }); describe("protobufjs", () => { test("generates code for a simple enum", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnum", "protobufjs"); + const code = generateEnumTypeCode("testapis.enums", "MyEnum"); expect(code).toMatchSnapshot(); }); test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified", "protobufjs"); + const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified"); expect(code).toMatchSnapshot(); }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum", "protobufjs"); + const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum"); expect(code).toMatchSnapshot(); }); }); From 0da95e9a6426e35b75fa2400644c93c767907a91 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 11:06:55 +0900 Subject: [PATCH 15/27] refactor(protoc-gen-nexus): update inputObjectType test to use plugin approach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use buildCodeGeneratorRequest and plugin to get proper Schema context - Change packageName type to TestapisPackage for type safety - Add partialInputs parameter to support partial input testing - Simplify partial input test case - Remove unnecessary plugin wrapper for partial inputs test Note: Similar to pothos, this uses the plugin approach as InputObjectType requires Schema context for proper type creation. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../dslgen/printers/inputObjectType.test.ts | 87 ++++--------------- 1 file changed, 16 insertions(+), 71 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts index 27f82bc1..cd6110e6 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts @@ -1,46 +1,28 @@ import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { buildCodeGeneratorRequest, TestapisPackage } from "@proto-graphql/testapis-proto"; import { - InputObjectType, + InputObjectType, + TypeOptions, collectTypesFromFile, createRegistryFromSchema, defaultScalarMapping } from "@proto-graphql/codegen-core"; import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; -import { code } from "ts-poet"; import { describe, expect, test } from "vitest"; import { createInputObjectTypeCode } from "./inputObjectType.js"; import type { NexusPrinterOptions } from "./util.js"; -// Helper to extract InputObjectType instances and generate code -function generateInputObjectTypeCode(packageName: string, typeName: string, options: NexusPrinterOptions) { - const req = buildCodeGeneratorRequest(packageName); - - // Default type options - const typeOptions = { - partialInputs: false, +function generateInputObjectTypeCode(packageName: TestapisPackage, typeName: string, options: NexusPrinterOptions, partialInputs = false): string { + const typeOptions: TypeOptions = { + partialInputs, scalarMapping: defaultScalarMapping, ignoreNonMessageOneofFields: false, }; + + const req = buildCodeGeneratorRequest(packageName); - // Create a minimal plugin to get Schema + let result = ""; const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: createTsGenerator({ - generateFiles: (schema, file) => { - // Just return, we only need the schema - }, - dsl: "nexus", - }), - parseOptions: parseNexusOptions, - }); - - // Run the plugin to get the transformed schema - const resp = plugin.run(req); - - // Now we need to recreate the schema to access its internals - const testPlugin = createEcmaScriptPlugin({ name: "test-plugin", version: "v1.0.0", generateTs: (schema) => { @@ -50,22 +32,19 @@ function generateInputObjectTypeCode(packageName: string, typeName: string, opti const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); if (!targetFile) throw new Error(`File for ${packageName} not found`); + // Collect types from the file const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); + const inputType = types.find(t => t.typeName === typeName && t instanceof InputObjectType) as InputObjectType; if (!inputType) throw new Error(`${typeName} type not found`); - - const generatedCode = createInputObjectTypeCode(inputType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); + + result = createInputObjectTypeCode(inputType, registry, options).toString(); }, parseOptions: parseNexusOptions, }); - testPlugin.run(req); - const result = (global as any).testResult; - delete (global as any).testResult; + plugin.run(req); return result; } @@ -148,42 +127,8 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for partial input types", () => { - const req = buildCodeGeneratorRequest("testapis.primitives"); - - // Create type options with partialInputs enabled - const typeOptions = { - partialInputs: true, - scalarMapping: defaultScalarMapping, - ignoreNonMessageOneofFields: false, - }; - - const testPlugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - const targetFile = schema.allFiles.find(f => f.name.includes("primitives")); - if (!targetFile) throw new Error(`File for primitives not found`); - - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - const partialInputType = types.find(t => t.typeName === "MessagePartialInput" && t instanceof InputObjectType) as InputObjectType; - if (!partialInputType) throw new Error(`MessagePartialInput type not found`); - - const generatedCode = createInputObjectTypeCode(partialInputType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); - }, - parseOptions: parseNexusOptions, - }); - - testPlugin.run(req); - - const result = (global as any).testResult; - delete (global as any).testResult; - - expect(result).toMatchSnapshot(); + const code = generateInputObjectTypeCode("testapis.primitives", "MessagePartialInput", options, true); + expect(code).toMatchSnapshot(); }); }); }); \ No newline at end of file From 94737383e2b1d3587a6ceed3814554df74f79c33 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 11:09:53 +0900 Subject: [PATCH 16/27] refactor(protoc-gen-nexus): simplify oneofUnionType test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use buildCodeGeneratorRequest and createEcmaScriptPlugin - Change packageName type to TestapisPackage for type safety - Remove unnecessary plugin wrapper and global variable hack - Keep special handling for import_squashed_union.pkg1 edge case 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../dslgen/printers/oneofUnionType.test.ts | 42 ++++--------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts index e1e0b9aa..40ddaa97 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts @@ -1,47 +1,29 @@ import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { buildCodeGeneratorRequest, TestapisPackage } from "@proto-graphql/testapis-proto"; import { OneofUnionType, SquashedOneofUnionType, + TypeOptions, collectTypesFromFile, createRegistryFromSchema, defaultScalarMapping } from "@proto-graphql/codegen-core"; import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; -import { code } from "ts-poet"; import { describe, expect, test } from "vitest"; import { createOneofUnionTypeCode } from "./oneofUnionType.js"; import type { NexusPrinterOptions } from "./util.js"; -// Helper to extract OneofUnionType instances and generate code -function generateOneofUnionTypeCode(packageName: string, typeName: string, options: NexusPrinterOptions) { - const req = buildCodeGeneratorRequest(packageName); - - // Default type options - const typeOptions = { +function generateOneofUnionTypeCode(packageName: TestapisPackage, typeName: string, options: NexusPrinterOptions): string { + const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: defaultScalarMapping, ignoreNonMessageOneofFields: false, }; - // Create a minimal plugin to get Schema - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: createTsGenerator({ - generateFiles: (schema, file) => { - // Just return, we only need the schema - }, - dsl: "nexus", - }), - parseOptions: parseNexusOptions, - }); - - // Run the plugin to get the transformed schema - const resp = plugin.run(req); + const req = buildCodeGeneratorRequest(packageName); - // Now we need to recreate the schema to access its internals - const testPlugin = createEcmaScriptPlugin({ + let result = ""; + const plugin = createEcmaScriptPlugin({ name: "test-plugin", version: "v1.0.0", generateTs: (schema) => { @@ -61,18 +43,12 @@ function generateOneofUnionTypeCode(packageName: string, typeName: string, optio const oneofType = types.find(t => t.typeName === typeName && (t instanceof OneofUnionType || t instanceof SquashedOneofUnionType)) as OneofUnionType | SquashedOneofUnionType; if (!oneofType) throw new Error(`${typeName} type not found`); - const generatedCode = createOneofUnionTypeCode(oneofType, registry, options); - - // Store the result for testing - (global as any).testResult = generatedCode.toString(); + result = createOneofUnionTypeCode(oneofType, registry, options).toString(); }, parseOptions: parseNexusOptions, }); - testPlugin.run(req); - - const result = (global as any).testResult; - delete (global as any).testResult; + plugin.run(req); return result; } From d47111b819b0d4ef98faecac0e017b1535b0e936 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 11:31:26 +0900 Subject: [PATCH 17/27] refactor(protoc-gen-pothos): simplify inputObjectType test to use direct message construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create InputObjectType directly from descriptor message - Remove plugin wrapper completely and use registry directly - Use inputType.toPartialInput() for partial inputs - Rename parameter to typeNameInProto to clarify proto message name usage - Update test cases to use actual proto message names instead of GraphQL type names 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../dslgen/printers/inputObjectType.test.ts | 65 +++++++------------ 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts index dd9f1d43..f5c364e6 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts @@ -1,5 +1,5 @@ import { getTestapisFileDescriptorSet, TestapisPackage, buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; -import { +import { InputObjectType, ObjectType, TypeOptions, @@ -15,40 +15,25 @@ import { describe, expect, test } from "vitest"; import { createInputObjectTypeCode } from "./inputObjectType.js"; import type { PothosPrinterOptions } from "./util.js"; -function generateInputObjectTypeCode(packageName: TestapisPackage, typeName: string, options: PothosPrinterOptions, partialInputs = false): string { +function generateInputObjectTypeCode(packageName: TestapisPackage, typeNameInProto: string, options: PothosPrinterOptions, partialInputs = false): string { const typeOptions: TypeOptions = { partialInputs, scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; - const req = buildCodeGeneratorRequest(packageName); - - // We need to run through the plugin to get a proper Schema - let result = ""; - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - // Find the target file - const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); - if (!targetFile) throw new Error(`File for ${packageName} not found`); - - // Collect types from the file - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - - const inputType = types.find(t => t.typeName === typeName && t instanceof InputObjectType) as InputObjectType; - if (!inputType) throw new Error(`${typeName} type not found`); - - result = createInputObjectTypeCode(inputType, registry, options).toString(); - }, - parseOptions: parsePothosOptions, - }); - - plugin.run(req); - return result; + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet) + const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); + if (descMsg === undefined) { + throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + } + + const inputType = new InputObjectType(descMsg, typeOptions); + + const code = createInputObjectTypeCode(partialInputs ? inputType.toPartialInput() : inputType, registry, options) + + return code.toString(); } describe("createInputObjectTypeCode", () => { @@ -66,27 +51,27 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "PrimitivesInput", options); + const code = generateInputObjectTypeCode("testapis.primitives", "Primitives", options); expect(code).toMatchSnapshot(); }); test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "MessageInput", options); + const code = generateInputObjectTypeCode("testapis.primitives", "Message", options); expect(code).toMatchSnapshot(); }); test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode("testapis.oneof", "OneofParentInput", options); + const code = generateInputObjectTypeCode("testapis.oneof", "OneofParent", options); expect(code).toMatchSnapshot(); }); test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessageInput", options); + const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessage", options); expect(code).toMatchSnapshot(); }); test("generates code for nested input types", () => { - const code = generateInputObjectTypeCode("testapis.nested", "ParentMessageInput", options); + const code = generateInputObjectTypeCode("testapis.nested", "ParentMessage", options); expect(code).toMatchSnapshot(); }); }); @@ -105,22 +90,22 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "PrimitivesInput", options); + const code = generateInputObjectTypeCode("testapis.primitives", "Primitives", options); expect(code).toMatchSnapshot(); }); test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "MessageInput", options); + const code = generateInputObjectTypeCode("testapis.primitives", "Message", options); expect(code).toMatchSnapshot(); }); test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode("testapis.oneof", "OneofParentInput", options); + const code = generateInputObjectTypeCode("testapis.oneof", "OneofParent", options); expect(code).toMatchSnapshot(); }); test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessageInput", options); + const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessage", options); expect(code).toMatchSnapshot(); }); }); @@ -139,8 +124,8 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for partial input types", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "MessagePartialInput", options, true); + const code = generateInputObjectTypeCode("testapis.primitives", "Message", options, true); expect(code).toMatchSnapshot(); }); }); -}); \ No newline at end of file +}); From d40f687f7b8e6bc72fb4902c0300f5a4f514d14b Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 11:52:28 +0900 Subject: [PATCH 18/27] refactor(protoc-gen-nexus): simplify inputObjectType test to use direct message construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create InputObjectType directly from descriptor message - Remove plugin wrapper completely and use registry directly - Use inputType.toPartialInput() for partial inputs - Rename parameter to typeNameInProto to clarify proto message name usage - Update test cases to use actual proto message names instead of GraphQL type names 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../dslgen/printers/inputObjectType.test.ts | 68 +++++++------------ 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts index cd6110e6..ce5dc412 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts @@ -1,51 +1,33 @@ -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest, TestapisPackage } from "@proto-graphql/testapis-proto"; +import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; import { InputObjectType, TypeOptions, - collectTypesFromFile, - createRegistryFromSchema, defaultScalarMapping } from "@proto-graphql/codegen-core"; -import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { createFileRegistry } from "@bufbuild/protobuf"; import { describe, expect, test } from "vitest"; import { createInputObjectTypeCode } from "./inputObjectType.js"; import type { NexusPrinterOptions } from "./util.js"; -function generateInputObjectTypeCode(packageName: TestapisPackage, typeName: string, options: NexusPrinterOptions, partialInputs = false): string { +function generateInputObjectTypeCode(packageName: TestapisPackage, typeNameInProto: string, options: NexusPrinterOptions, partialInputs = false): string { const typeOptions: TypeOptions = { partialInputs, scalarMapping: defaultScalarMapping, ignoreNonMessageOneofFields: false, }; - const req = buildCodeGeneratorRequest(packageName); - - let result = ""; - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - // Find the target file - const targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); - if (!targetFile) throw new Error(`File for ${packageName} not found`); - - // Collect types from the file - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - - const inputType = types.find(t => t.typeName === typeName && t instanceof InputObjectType) as InputObjectType; - if (!inputType) throw new Error(`${typeName} type not found`); - - result = createInputObjectTypeCode(inputType, registry, options).toString(); - }, - parseOptions: parseNexusOptions, - }); - - - plugin.run(req); - return result; + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet); + const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); + if (descMsg === undefined) { + throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + } + + const inputType = new InputObjectType(descMsg, typeOptions); + + const code = createInputObjectTypeCode(partialInputs ? inputType.toPartialInput() : inputType, registry, options); + + return code.toString(); } describe("createInputObjectTypeCode", () => { @@ -60,27 +42,27 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "PrimitivesInput", options); + const code = generateInputObjectTypeCode("testapis.primitives", "Primitives", options); expect(code).toMatchSnapshot(); }); test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "MessageInput", options); + const code = generateInputObjectTypeCode("testapis.primitives", "Message", options); expect(code).toMatchSnapshot(); }); test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode("testapis.oneof", "OneofParentInput", options); + const code = generateInputObjectTypeCode("testapis.oneof", "OneofParent", options); expect(code).toMatchSnapshot(); }); test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessageInput", options); + const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessage", options); expect(code).toMatchSnapshot(); }); test("generates code for nested input types", () => { - const code = generateInputObjectTypeCode("testapis.nested", "ParentMessageInput", options); + const code = generateInputObjectTypeCode("testapis.nested", "ParentMessage", options); expect(code).toMatchSnapshot(); }); }); @@ -96,22 +78,22 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "PrimitivesInput", options); + const code = generateInputObjectTypeCode("testapis.primitives", "Primitives", options); expect(code).toMatchSnapshot(); }); test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "MessageInput", options); + const code = generateInputObjectTypeCode("testapis.primitives", "Message", options); expect(code).toMatchSnapshot(); }); test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode("testapis.oneof", "OneofParentInput", options); + const code = generateInputObjectTypeCode("testapis.oneof", "OneofParent", options); expect(code).toMatchSnapshot(); }); test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessageInput", options); + const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessage", options); expect(code).toMatchSnapshot(); }); }); @@ -127,7 +109,7 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for partial input types", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "MessagePartialInput", options, true); + const code = generateInputObjectTypeCode("testapis.primitives", "Message", options, true); expect(code).toMatchSnapshot(); }); }); From 7776f62b12ebf52278b327aa87c73a5073cd758e Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 12:16:06 +0900 Subject: [PATCH 19/27] refactor(protoc-gen-pothos): simplify oneofUnionType test to use direct descriptor construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create OneofUnionType directly from descriptor oneof field - Create SquashedOneofUnionType directly from descriptor message - Remove plugin wrapper completely and use registry directly - Add explicit oneofFieldName parameter for clarity - Split into two functions for regular and squashed unions - Update test cases to use actual proto message and field names 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../dslgen/printers/oneofUnionType.test.ts | 97 ++++++++++--------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts index 1c6fd0c0..bd72a84c 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts @@ -1,6 +1,6 @@ import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest, TestapisPackage } from "@proto-graphql/testapis-proto"; -import { +import { buildCodeGeneratorRequest, getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; +import { OneofUnionType, SquashedOneofUnionType, TypeOptions, @@ -13,46 +13,53 @@ import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plu import { describe, expect, test } from "vitest"; import { createOneofUnionTypeCode } from "./oneofUnionType.js"; import type { PothosPrinterOptions } from "./util.js"; +import { createFileRegistry } from "@bufbuild/protobuf"; -function generateOneofUnionTypeCode(packageName: TestapisPackage, typeName: string, options: PothosPrinterOptions): string { +function generateOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProto: string, oneofFieldName: string, options: PothosPrinterOptions): string { const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; - - const req = buildCodeGeneratorRequest(packageName); - - let result = ""; - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - // Find the target file - let targetFile; - - // For the import_oneof_member_from_other_file test, we need to check the parent.proto file - if (packageName === "testapis.edgecases.import_oneof_member_from_other_file") { - targetFile = schema.allFiles.find(f => f.name.includes("import_oneof_member_from_other_file/parent")); - } else { - targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); - } - - if (!targetFile) throw new Error(`File for ${packageName} not found`); - - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - const oneofType = types.find(t => t.typeName === typeName && (t instanceof OneofUnionType || t instanceof SquashedOneofUnionType)); - if (!oneofType) throw new Error(`${typeName} type not found`); - - result = createOneofUnionTypeCode(oneofType as OneofUnionType | SquashedOneofUnionType, registry, options).toString(); - }, - parseOptions: parsePothosOptions, - }); - - plugin.run(req); - return result; + + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet) + const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); + if (descMsg === undefined) { + throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + } + + const descOneof = descMsg.oneofs.find(d => d.name === oneofFieldName) + if (descOneof === undefined) { + throw new Error(`Oneof field ${oneofFieldName} not found in message ${typeNameInProto} in package ${packageName}`); + } + + const oneofType = new OneofUnionType(descOneof, typeOptions); + + const code = createOneofUnionTypeCode(oneofType, registry, options).toString(); + + return code.toString(); +} + +function generateSquashedOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProto: string, options: PothosPrinterOptions): string { + const typeOptions: TypeOptions = { + partialInputs: false, + scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet) + const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); + if (descMsg === undefined) { + throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + } + + const oneofType = new SquashedOneofUnionType(descMsg, typeOptions); + + const code = createOneofUnionTypeCode(oneofType, registry, options).toString(); + + return code.toString(); } describe("createOneofUnionTypeCode", () => { @@ -70,22 +77,22 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code for a required oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); expect(code).toMatchSnapshot(); }); test("generates code for an optional oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentOptionalOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "optional_oneof_members", options); expect(code).toMatchSnapshot(); }); test("generates code for a squashed oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.extensions", "TestPrefixPrefixedMessageSquashedMessage", options); + const code = generateSquashedOneofUnionTypeCode("testapis.extensions", "PrefixedMessage.SquashedMessage", options); expect(code).toMatchSnapshot(); }); test("generates code for imported oneof member", () => { - const code = generateOneofUnionTypeCode("testapis.edgecases.import_oneof_member_from_other_file", "OneofParentOneofField", options); + const code = generateOneofUnionTypeCode("testapis.edgecases.import_oneof_member_from_other_file", "OneofParent", "oneof_field", options); expect(code).toMatchSnapshot(); }); }); @@ -104,17 +111,17 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code for a required oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); expect(code).toMatchSnapshot(); }); test("generates code for an optional oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentOptionalOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "optional_oneof_members", options); expect(code).toMatchSnapshot(); }); test("generates code for a squashed oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.extensions", "TestPrefixPrefixedMessageSquashedMessage", options); + const code = generateSquashedOneofUnionTypeCode("testapis.extensions", "PrefixedMessage.SquashedMessage", options); expect(code).toMatchSnapshot(); }); }); @@ -133,8 +140,8 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code with correct imports for graphql_type layout", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); expect(code).toMatchSnapshot(); }); }); -}); \ No newline at end of file +}); From add22eb3034e765dd398018e81f2fa5644cf1f0b Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 12:21:55 +0900 Subject: [PATCH 20/27] refactor(protoc-gen-nexus): refactor oneofUnionType test to use direct descriptor construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace plugin wrapper approach with getTestapisFileDescriptorSet and createFileRegistry - Create OneofUnionType directly from descriptor oneof field - Add explicit oneofFieldName parameter to generateOneofUnionTypeCode - Split generation logic into generateOneofUnionTypeCode and generateSquashedOneofUnionTypeCode - Update test calls to use proto message names (OneofParent) and oneof field names 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../dslgen/printers/oneofUnionType.test.ts | 91 ++++++++++--------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts index 40ddaa97..6c81bcd1 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts @@ -1,55 +1,60 @@ -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest, TestapisPackage } from "@proto-graphql/testapis-proto"; +import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; import { OneofUnionType, SquashedOneofUnionType, TypeOptions, - collectTypesFromFile, - createRegistryFromSchema, defaultScalarMapping } from "@proto-graphql/codegen-core"; -import { createTsGenerator, parseNexusOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { createFileRegistry } from "@bufbuild/protobuf"; import { describe, expect, test } from "vitest"; import { createOneofUnionTypeCode } from "./oneofUnionType.js"; import type { NexusPrinterOptions } from "./util.js"; -function generateOneofUnionTypeCode(packageName: TestapisPackage, typeName: string, options: NexusPrinterOptions): string { +function generateOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProto: string, oneofFieldName: string, options: NexusPrinterOptions): string { const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: defaultScalarMapping, ignoreNonMessageOneofFields: false, }; - - const req = buildCodeGeneratorRequest(packageName); - - let result = ""; - const plugin = createEcmaScriptPlugin({ - name: "test-plugin", - version: "v1.0.0", - generateTs: (schema) => { - const registry = createRegistryFromSchema(schema); - - // Find the target file - let targetFile = schema.allFiles.find(f => f.name.includes(packageName.split('.')[1])); - - // Special handling for edge cases - if (packageName === "testapis.edgecases.import_squashed_union.pkg1") { - targetFile = schema.allFiles.find(f => f.name.includes("import_squashed_union/pkg1/types")); - } - - if (!targetFile) throw new Error(`File for ${packageName} not found`); - - const types = collectTypesFromFile(targetFile, typeOptions, schema.allFiles); - const oneofType = types.find(t => t.typeName === typeName && (t instanceof OneofUnionType || t instanceof SquashedOneofUnionType)) as OneofUnionType | SquashedOneofUnionType; - if (!oneofType) throw new Error(`${typeName} type not found`); - - result = createOneofUnionTypeCode(oneofType, registry, options).toString(); - }, - parseOptions: parseNexusOptions, - }); - - plugin.run(req); - return result; + + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet) + const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); + if (descMsg === undefined) { + throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + } + + const descOneof = descMsg.oneofs.find(d => d.name === oneofFieldName) + if (descOneof === undefined) { + throw new Error(`Oneof field ${oneofFieldName} not found in message ${typeNameInProto} in package ${packageName}`); + } + + const oneofType = new OneofUnionType(descOneof, typeOptions); + + const code = createOneofUnionTypeCode(oneofType, registry, options).toString(); + + return code.toString(); +} + +function generateSquashedOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProto: string, options: NexusPrinterOptions): string { + const typeOptions: TypeOptions = { + partialInputs: false, + scalarMapping: defaultScalarMapping, + ignoreNonMessageOneofFields: false, + }; + + const descSet = getTestapisFileDescriptorSet(packageName); + const registry = createFileRegistry(descSet) + const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); + if (descMsg === undefined) { + throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + } + + const oneofType = new SquashedOneofUnionType(descMsg, typeOptions); + + const code = createOneofUnionTypeCode(oneofType, registry, options).toString(); + + return code.toString(); } describe("createOneofUnionTypeCode", () => { @@ -64,17 +69,17 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code for a simple oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); expect(code).toMatchSnapshot(); }); test("generates code for optional oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentOptionalOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "optional_oneof_members", options); expect(code).toMatchSnapshot(); }); test("generates code for import squashed union", () => { - const code = generateOneofUnionTypeCode("testapis.edgecases.import_squashed_union.pkg1", "SquashedOneof", options); + const code = generateSquashedOneofUnionTypeCode("testapis.edgecases.import_squashed_union.pkg1", "SquashedOneof", options); expect(code).toMatchSnapshot(); }); }); @@ -90,17 +95,17 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code for a simple oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentRequiredOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); expect(code).toMatchSnapshot(); }); test("generates code for optional oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParentOptionalOneofMembers", options); + const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "optional_oneof_members", options); expect(code).toMatchSnapshot(); }); test("generates code for import squashed union", () => { - const code = generateOneofUnionTypeCode("testapis.edgecases.import_squashed_union.pkg1", "SquashedOneof", options); + const code = generateSquashedOneofUnionTypeCode("testapis.edgecases.import_squashed_union.pkg1", "SquashedOneof", options); expect(code).toMatchSnapshot(); }); }); From 2c83dfec9a7d6a2ce58c7f816010601d180c8233 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 12:33:23 +0900 Subject: [PATCH 21/27] refactor(protoc-gen-pothos): use Protobuf enum names with dot notation in enumType test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change parameter name from enumTypeName to enumTypeNameInProto for clarity - Use dot notation for nested enums (e.g., ParentMessage.NestedEnum) - Simplify enum lookup logic to directly use proto names - Add special handling for testapi.enums package name discrepancy 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/dslgen/printers/enumType.test.ts | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts index 02aee645..537d6d44 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts @@ -10,7 +10,7 @@ import { describe, expect, test } from "vitest"; import { createEnumTypeCode } from "./enumType.js"; import type { PothosPrinterOptions } from "./util.js"; -function generateEnumTypeCode(packageName: TestapisPackage, enumTypeName: string, options: PothosPrinterOptions): string { +function generateEnumTypeCode(packageName: TestapisPackage, enumTypeNameInProto: string, options: PothosPrinterOptions): string { const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, @@ -19,23 +19,18 @@ function generateEnumTypeCode(packageName: TestapisPackage, enumTypeName: string const descSet = getTestapisFileDescriptorSet(packageName); const registry = createFileRegistry(descSet); - // For nested enums, we need to check the parent message - let descEnum = registry.getEnum(`${packageName}.${enumTypeName}`); - // If not found directly, it might be a nested enum - if (descEnum === undefined && enumTypeName.includes("NestedEnum")) { - const parentMessageName = enumTypeName.replace("NestedEnum", ""); - descEnum = registry.getEnum(`${packageName}.${parentMessageName}.NestedEnum`); - } + // The actual proto package might differ from the TestapisPackage key + // For example: "testapis.enums" key but "testapi.enums" proto package + let descEnum = registry.getEnum(`${packageName}.${enumTypeNameInProto}`); - if (descEnum === undefined) { - // Try with the actual proto package name (testapi instead of testapis) - const actualPackageName = packageName.replace("testapis", "testapi"); - descEnum = registry.getEnum(`${actualPackageName}.${enumTypeName}`); + if (descEnum === undefined && packageName === "testapis.enums") { + // Try with the actual proto package name + descEnum = registry.getEnum(`testapi.enums.${enumTypeNameInProto}`); } if (descEnum === undefined) { - throw new Error(`Enum ${enumTypeName} not found in package ${packageName}`); + throw new Error(`Enum ${enumTypeNameInProto} not found in package ${packageName}`); } const enumType = new EnumType(descEnum, typeOptions); @@ -70,7 +65,7 @@ describe("createEnumTypeCode", () => { }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum", options); + const code = generateEnumTypeCode("testapis.nested", "ParentMessage.NestedEnum", options); expect(code).toMatchSnapshot(); }); @@ -104,7 +99,7 @@ describe("createEnumTypeCode", () => { }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum", options); + const code = generateEnumTypeCode("testapis.nested", "ParentMessage.NestedEnum", options); expect(code).toMatchSnapshot(); }); }); From 9a3f024cc39899ecdcbb0d4e451f4c4518e30578 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 12:43:48 +0900 Subject: [PATCH 22/27] refactor(protoc-gen-nexus): use Protobuf enum names with dot notation in enumType test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change parameter name from enumTypeName to enumTypeNameInProto for clarity - Use dot notation for nested enums (e.g., ParentMessage.NestedEnum) - Simplify enum lookup logic to directly use proto names - Add special handling for testapi.enums package name discrepancy 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../src/dslgen/printers/enumType.test.ts | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts index c552fe76..823515c6 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts @@ -8,7 +8,7 @@ import { createFileRegistry } from "@bufbuild/protobuf"; import { describe, expect, test } from "vitest"; import { createEnumTypeCode } from "./enumType.js"; -function generateEnumTypeCode(packageName: TestapisPackage, enumTypeName: string): string { +function generateEnumTypeCode(packageName: TestapisPackage, enumTypeNameInProto: string): string { const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: defaultScalarMapping, @@ -18,23 +18,17 @@ function generateEnumTypeCode(packageName: TestapisPackage, enumTypeName: string const descSet = getTestapisFileDescriptorSet(packageName); const registry = createFileRegistry(descSet); - // For nested enums, we need to check the parent message - let descEnum = registry.getEnum(`${packageName}.${enumTypeName}`); + // The actual proto package might differ from the TestapisPackage key + // For example: "testapis.enums" key but "testapi.enums" proto package + let descEnum = registry.getEnum(`${packageName}.${enumTypeNameInProto}`); - // If not found directly, it might be a nested enum - if (descEnum === undefined && enumTypeName.includes("NestedEnum")) { - const parentMessageName = enumTypeName.replace("NestedEnum", ""); - descEnum = registry.getEnum(`${packageName}.${parentMessageName}.NestedEnum`); + if (descEnum === undefined && packageName === "testapis.enums") { + // Try with the actual proto package name + descEnum = registry.getEnum(`testapi.enums.${enumTypeNameInProto}`); } if (descEnum === undefined) { - // Try with the actual proto package name (testapi instead of testapis) - const actualPackageName = packageName.replace("testapis", "testapi"); - descEnum = registry.getEnum(`${actualPackageName}.${enumTypeName}`); - } - - if (descEnum === undefined) { - throw new Error(`Enum ${enumTypeName} not found in package ${packageName}`); + throw new Error(`Enum ${enumTypeNameInProto} not found in package ${packageName}`); } const enumType = new EnumType(descEnum, typeOptions); @@ -57,7 +51,7 @@ describe("createEnumTypeCode", () => { }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum"); + const code = generateEnumTypeCode("testapis.nested", "ParentMessage.NestedEnum"); expect(code).toMatchSnapshot(); }); @@ -79,7 +73,7 @@ describe("createEnumTypeCode", () => { }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessageNestedEnum"); + const code = generateEnumTypeCode("testapis.nested", "ParentMessage.NestedEnum"); expect(code).toMatchSnapshot(); }); }); From d6744915f0b55b172b4bf1c842237a3608f4884b Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 12:45:32 +0900 Subject: [PATCH 23/27] remove unnecessary non-null assertion --- .../protoc-gen-pothos/src/dslgen/printers/objectType.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts index db871bb6..062101fe 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts @@ -29,7 +29,7 @@ function generateObjectTypeCode(packageName: TestapisPackage, messageTypeName: s throw new Error(`Message ${messageTypeName} not found in package ${packageName}`); } - const objType = new ObjectType(descMsg!, typeOptions) + const objType = new ObjectType(descMsg, typeOptions); const code = createObjectTypeCode(objType, registry, options) From 3a13dfaa5e7a74752c648db13cd95106f60cb17b Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 12:45:52 +0900 Subject: [PATCH 24/27] `pnpm lint` --- .../@proto-graphql/codegen-core/package.json | 15 +-- .../protoc-plugin-helpers/package.json | 15 +-- .../scalars-protobuf-es/package.json | 14 +- .../proto-fields-plugin/package.json | 15 +-- packages/protoc-gen-nexus/package.json | 16 +-- .../src/dslgen/printers/enumType.test.ts | 50 ++++--- .../dslgen/printers/inputObjectType.test.ts | 93 ++++++++++--- .../src/dslgen/printers/objectType.test.ts | 61 +++++++-- .../dslgen/printers/oneofUnionType.test.ts | 98 ++++++++++---- packages/protoc-gen-pothos/package.json | 16 +-- .../src/dslgen/printers/enumType.test.ts | 66 +++++++--- .../dslgen/printers/inputObjectType.test.ts | 101 +++++++++++---- .../src/dslgen/printers/objectType.test.ts | 91 +++++++++---- .../dslgen/printers/oneofUnionType.test.ts | 122 ++++++++++++++---- 14 files changed, 531 insertions(+), 242 deletions(-) diff --git a/packages/@proto-graphql/codegen-core/package.json b/packages/@proto-graphql/codegen-core/package.json index f40de95b..4f0a959a 100644 --- a/packages/@proto-graphql/codegen-core/package.json +++ b/packages/@proto-graphql/codegen-core/package.json @@ -2,13 +2,7 @@ "name": "@proto-graphql/codegen-core", "version": "0.5.2", "description": "", - "keywords": [ - "graphql", - "grpc", - "nexus", - "protobuf", - "typescript" - ], + "keywords": ["graphql", "grpc", "nexus", "protobuf", "typescript"], "repository": "git@github.com:proto-graphql/proto-graphql-js.git", "author": "izumin5210 ", "license": "MIT", @@ -28,12 +22,7 @@ } } }, - "files": [ - "src/", - "dist/", - "!src/**/*.test.ts", - "!src/**/__tests__/" - ], + "files": ["src/", "dist/", "!src/**/*.test.ts", "!src/**/__tests__/"], "sideEffects": false, "private": false, "publishConfig": { diff --git a/packages/@proto-graphql/protoc-plugin-helpers/package.json b/packages/@proto-graphql/protoc-plugin-helpers/package.json index 3237826c..6911c647 100644 --- a/packages/@proto-graphql/protoc-plugin-helpers/package.json +++ b/packages/@proto-graphql/protoc-plugin-helpers/package.json @@ -2,13 +2,7 @@ "name": "@proto-graphql/protoc-plugin-helpers", "version": "0.4.2", "description": "", - "keywords": [ - "graphql", - "grpc", - "nexus", - "protobuf", - "typescript" - ], + "keywords": ["graphql", "grpc", "nexus", "protobuf", "typescript"], "repository": "git@github.com:proto-graphql/proto-graphql-js.git", "author": "izumin5210 ", "license": "MIT", @@ -28,12 +22,7 @@ } } }, - "files": [ - "src/", - "dist/", - "!src/**/*.test.ts", - "!src/**/__tests__/" - ], + "files": ["src/", "dist/", "!src/**/*.test.ts", "!src/**/__tests__/"], "sideEffects": false, "private": false, "publishConfig": { diff --git a/packages/@proto-graphql/scalars-protobuf-es/package.json b/packages/@proto-graphql/scalars-protobuf-es/package.json index 4fde49a1..a337811b 100644 --- a/packages/@proto-graphql/scalars-protobuf-es/package.json +++ b/packages/@proto-graphql/scalars-protobuf-es/package.json @@ -2,12 +2,7 @@ "name": "@proto-graphql/scalars-protobuf-es", "version": "0.4.2", "description": "", - "keywords": [ - "graphql", - "grpc", - "protobuf", - "typescript" - ], + "keywords": ["graphql", "grpc", "protobuf", "typescript"], "repository": "git@github.com:proto-graphql/proto-graphql-js.git", "author": "izumin5210 ", "license": "MIT", @@ -27,12 +22,7 @@ } } }, - "files": [ - "src/", - "dist/", - "!src/**/*.test.ts", - "!src/**/__tests__/" - ], + "files": ["src/", "dist/", "!src/**/*.test.ts", "!src/**/__tests__/"], "sideEffects": false, "private": false, "publishConfig": { diff --git a/packages/@proto-nexus/proto-fields-plugin/package.json b/packages/@proto-nexus/proto-fields-plugin/package.json index b0b8d2a3..67af43d0 100644 --- a/packages/@proto-nexus/proto-fields-plugin/package.json +++ b/packages/@proto-nexus/proto-fields-plugin/package.json @@ -2,13 +2,7 @@ "name": "@proto-nexus/proto-fields-plugin", "version": "0.5.6", "description": "Nexus plugin for building subset types from proto-nexus's artifacts", - "keywords": [ - "graphql", - "grpc", - "nexus", - "protobuf", - "typescript" - ], + "keywords": ["graphql", "grpc", "nexus", "protobuf", "typescript"], "repository": "git@github.com:proto-graphql/proto-graphql-js.git", "author": "izumin5210 ", "license": "MIT", @@ -30,12 +24,7 @@ "engines": { "node": ">= 14.0.0" }, - "files": [ - "src/", - "dist/", - "!src/**/*.test.ts", - "!src/**/__tests__/" - ], + "files": ["src/", "dist/", "!src/**/*.test.ts", "!src/**/__tests__/"], "peerDependencies": { "nexus": "^1.0.0" }, diff --git a/packages/protoc-gen-nexus/package.json b/packages/protoc-gen-nexus/package.json index 09a8bdec..e0355e98 100644 --- a/packages/protoc-gen-nexus/package.json +++ b/packages/protoc-gen-nexus/package.json @@ -2,13 +2,7 @@ "name": "protoc-gen-nexus", "version": "0.8.2", "description": "Generate DSL for GraphQL Nexus from Protocol Buffers IDL", - "keywords": [ - "graphql", - "grpc", - "nexus", - "protobuf", - "typescript" - ], + "keywords": ["graphql", "grpc", "nexus", "protobuf", "typescript"], "bin": { "protoc-gen-nexus": "./bin/protoc-gen-nexus.cjs" }, @@ -31,13 +25,7 @@ } } }, - "files": [ - "bin/", - "src/", - "dist/", - "!src/**/*.test.ts", - "!src/**/__tests__/" - ], + "files": ["bin/", "src/", "dist/", "!src/**/*.test.ts", "!src/**/__tests__/"], "sideEffects": false, "private": false, "publishConfig": { diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts index 823515c6..6203b6cb 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts @@ -1,14 +1,20 @@ -import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; -import { +import { createFileRegistry } from "@bufbuild/protobuf"; +import { EnumType, - TypeOptions, - defaultScalarMapping + type TypeOptions, + defaultScalarMapping, } from "@proto-graphql/codegen-core"; -import { createFileRegistry } from "@bufbuild/protobuf"; +import { + type TestapisPackage, + getTestapisFileDescriptorSet, +} from "@proto-graphql/testapis-proto"; import { describe, expect, test } from "vitest"; import { createEnumTypeCode } from "./enumType.js"; -function generateEnumTypeCode(packageName: TestapisPackage, enumTypeNameInProto: string): string { +function generateEnumTypeCode( + packageName: TestapisPackage, + enumTypeNameInProto: string, +): string { const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: defaultScalarMapping, @@ -17,18 +23,20 @@ function generateEnumTypeCode(packageName: TestapisPackage, enumTypeNameInProto: const descSet = getTestapisFileDescriptorSet(packageName); const registry = createFileRegistry(descSet); - + // The actual proto package might differ from the TestapisPackage key // For example: "testapis.enums" key but "testapi.enums" proto package let descEnum = registry.getEnum(`${packageName}.${enumTypeNameInProto}`); - + if (descEnum === undefined && packageName === "testapis.enums") { // Try with the actual proto package name descEnum = registry.getEnum(`testapi.enums.${enumTypeNameInProto}`); } - + if (descEnum === undefined) { - throw new Error(`Enum ${enumTypeNameInProto} not found in package ${packageName}`); + throw new Error( + `Enum ${enumTypeNameInProto} not found in package ${packageName}`, + ); } const enumType = new EnumType(descEnum, typeOptions); @@ -46,12 +54,18 @@ describe("createEnumTypeCode", () => { }); test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified"); + const code = generateEnumTypeCode( + "testapis.enums", + "MyEnumWithoutUnspecified", + ); expect(code).toMatchSnapshot(); }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessage.NestedEnum"); + const code = generateEnumTypeCode( + "testapis.nested", + "ParentMessage.NestedEnum", + ); expect(code).toMatchSnapshot(); }); @@ -68,13 +82,19 @@ describe("createEnumTypeCode", () => { }); test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified"); + const code = generateEnumTypeCode( + "testapis.enums", + "MyEnumWithoutUnspecified", + ); expect(code).toMatchSnapshot(); }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessage.NestedEnum"); + const code = generateEnumTypeCode( + "testapis.nested", + "ParentMessage.NestedEnum", + ); expect(code).toMatchSnapshot(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts index ce5dc412..c4ee282f 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts @@ -1,15 +1,23 @@ -import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; -import { +import { createFileRegistry } from "@bufbuild/protobuf"; +import { InputObjectType, - TypeOptions, - defaultScalarMapping + type TypeOptions, + defaultScalarMapping, } from "@proto-graphql/codegen-core"; -import { createFileRegistry } from "@bufbuild/protobuf"; +import { + type TestapisPackage, + getTestapisFileDescriptorSet, +} from "@proto-graphql/testapis-proto"; import { describe, expect, test } from "vitest"; import { createInputObjectTypeCode } from "./inputObjectType.js"; import type { NexusPrinterOptions } from "./util.js"; -function generateInputObjectTypeCode(packageName: TestapisPackage, typeNameInProto: string, options: NexusPrinterOptions, partialInputs = false): string { +function generateInputObjectTypeCode( + packageName: TestapisPackage, + typeNameInProto: string, + options: NexusPrinterOptions, + partialInputs = false, +): string { const typeOptions: TypeOptions = { partialInputs, scalarMapping: defaultScalarMapping, @@ -20,12 +28,18 @@ function generateInputObjectTypeCode(packageName: TestapisPackage, typeNameInPro const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { - throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + throw new Error( + `Message ${typeNameInProto} not found in package ${packageName}`, + ); } const inputType = new InputObjectType(descMsg, typeOptions); - const code = createInputObjectTypeCode(partialInputs ? inputType.toPartialInput() : inputType, registry, options); + const code = createInputObjectTypeCode( + partialInputs ? inputType.toPartialInput() : inputType, + registry, + options, + ); return code.toString(); } @@ -42,27 +56,47 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Primitives", options); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Primitives", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Message", options); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Message", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode("testapis.oneof", "OneofParent", options); + const code = generateInputObjectTypeCode( + "testapis.oneof", + "OneofParent", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessage", options); + const code = generateInputObjectTypeCode( + "testapis.empty_types", + "EmptyMessage", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for nested input types", () => { - const code = generateInputObjectTypeCode("testapis.nested", "ParentMessage", options); + const code = generateInputObjectTypeCode( + "testapis.nested", + "ParentMessage", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -78,22 +112,38 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Primitives", options); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Primitives", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Message", options); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Message", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode("testapis.oneof", "OneofParent", options); + const code = generateInputObjectTypeCode( + "testapis.oneof", + "OneofParent", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessage", options); + const code = generateInputObjectTypeCode( + "testapis.empty_types", + "EmptyMessage", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -109,8 +159,13 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for partial input types", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Message", options, true); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Message", + options, + true, + ); expect(code).toMatchSnapshot(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts index ec879ebc..89ba2362 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts @@ -1,15 +1,22 @@ -import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; -import { +import { createFileRegistry } from "@bufbuild/protobuf"; +import { ObjectType, - TypeOptions, - defaultScalarMapping + type TypeOptions, + defaultScalarMapping, } from "@proto-graphql/codegen-core"; -import { createFileRegistry } from "@bufbuild/protobuf"; +import { + type TestapisPackage, + getTestapisFileDescriptorSet, +} from "@proto-graphql/testapis-proto"; import { describe, expect, test } from "vitest"; import { createObjectTypeCode } from "./objectType.js"; import type { NexusPrinterOptions } from "./util.js"; -function generateObjectTypeCode(packageName: TestapisPackage, messageTypeName: string, options: NexusPrinterOptions): string { +function generateObjectTypeCode( + packageName: TestapisPackage, + messageTypeName: string, + options: NexusPrinterOptions, +): string { const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: defaultScalarMapping, @@ -20,7 +27,9 @@ function generateObjectTypeCode(packageName: TestapisPackage, messageTypeName: s const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${messageTypeName}`); if (descMsg === undefined) { - throw new Error(`Message ${messageTypeName} not found in package ${packageName}`); + throw new Error( + `Message ${messageTypeName} not found in package ${packageName}`, + ); } const objType = new ObjectType(descMsg, typeOptions); @@ -42,12 +51,20 @@ describe("createObjectTypeCode", () => { }; test("generates code for a simple message", () => { - const code = generateObjectTypeCode("testapis.primitives", "Primitives", options); + const code = generateObjectTypeCode( + "testapis.primitives", + "Primitives", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a message with nested fields", () => { - const code = generateObjectTypeCode("testapis.primitives", "Message", options); + const code = generateObjectTypeCode( + "testapis.primitives", + "Message", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -63,17 +80,29 @@ describe("createObjectTypeCode", () => { }; test("generates code for a simple message", () => { - const code = generateObjectTypeCode("testapis.primitives", "Primitives", options); + const code = generateObjectTypeCode( + "testapis.primitives", + "Primitives", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a message with nested fields", () => { - const code = generateObjectTypeCode("testapis.primitives", "Message", options); + const code = generateObjectTypeCode( + "testapis.primitives", + "Message", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a message with oneofs", () => { - const code = generateObjectTypeCode("testapis.oneof", "OneofParent", options); + const code = generateObjectTypeCode( + "testapis.oneof", + "OneofParent", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -89,8 +118,12 @@ describe("createObjectTypeCode", () => { }; test("generates code for message with field extensions", () => { - const code = generateObjectTypeCode("testapis.extensions", "PrefixedMessage", options); + const code = generateObjectTypeCode( + "testapis.extensions", + "PrefixedMessage", + options, + ); expect(code).toMatchSnapshot(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts index 6c81bcd1..ef084cef 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts @@ -1,16 +1,24 @@ -import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; -import { +import { createFileRegistry } from "@bufbuild/protobuf"; +import { OneofUnionType, SquashedOneofUnionType, - TypeOptions, - defaultScalarMapping + type TypeOptions, + defaultScalarMapping, } from "@proto-graphql/codegen-core"; -import { createFileRegistry } from "@bufbuild/protobuf"; +import { + type TestapisPackage, + getTestapisFileDescriptorSet, +} from "@proto-graphql/testapis-proto"; import { describe, expect, test } from "vitest"; import { createOneofUnionTypeCode } from "./oneofUnionType.js"; import type { NexusPrinterOptions } from "./util.js"; -function generateOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProto: string, oneofFieldName: string, options: NexusPrinterOptions): string { +function generateOneofUnionTypeCode( + packageName: TestapisPackage, + typeNameInProto: string, + oneofFieldName: string, + options: NexusPrinterOptions, +): string { const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: defaultScalarMapping, @@ -18,25 +26,37 @@ function generateOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProt }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet) + const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { - throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + throw new Error( + `Message ${typeNameInProto} not found in package ${packageName}`, + ); } - const descOneof = descMsg.oneofs.find(d => d.name === oneofFieldName) + const descOneof = descMsg.oneofs.find((d) => d.name === oneofFieldName); if (descOneof === undefined) { - throw new Error(`Oneof field ${oneofFieldName} not found in message ${typeNameInProto} in package ${packageName}`); + throw new Error( + `Oneof field ${oneofFieldName} not found in message ${typeNameInProto} in package ${packageName}`, + ); } const oneofType = new OneofUnionType(descOneof, typeOptions); - const code = createOneofUnionTypeCode(oneofType, registry, options).toString(); + const code = createOneofUnionTypeCode( + oneofType, + registry, + options, + ).toString(); return code.toString(); } -function generateSquashedOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProto: string, options: NexusPrinterOptions): string { +function generateSquashedOneofUnionTypeCode( + packageName: TestapisPackage, + typeNameInProto: string, + options: NexusPrinterOptions, +): string { const typeOptions: TypeOptions = { partialInputs: false, scalarMapping: defaultScalarMapping, @@ -44,15 +64,21 @@ function generateSquashedOneofUnionTypeCode(packageName: TestapisPackage, typeNa }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet) + const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { - throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + throw new Error( + `Message ${typeNameInProto} not found in package ${packageName}`, + ); } const oneofType = new SquashedOneofUnionType(descMsg, typeOptions); - const code = createOneofUnionTypeCode(oneofType, registry, options).toString(); + const code = createOneofUnionTypeCode( + oneofType, + registry, + options, + ).toString(); return code.toString(); } @@ -69,17 +95,31 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code for a simple oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "required_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for optional oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "optional_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "optional_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for import squashed union", () => { - const code = generateSquashedOneofUnionTypeCode("testapis.edgecases.import_squashed_union.pkg1", "SquashedOneof", options); + const code = generateSquashedOneofUnionTypeCode( + "testapis.edgecases.import_squashed_union.pkg1", + "SquashedOneof", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -95,18 +135,32 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code for a simple oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "required_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for optional oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "optional_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "optional_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for import squashed union", () => { - const code = generateSquashedOneofUnionTypeCode("testapis.edgecases.import_squashed_union.pkg1", "SquashedOneof", options); + const code = generateSquashedOneofUnionTypeCode( + "testapis.edgecases.import_squashed_union.pkg1", + "SquashedOneof", + options, + ); expect(code).toMatchSnapshot(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-pothos/package.json b/packages/protoc-gen-pothos/package.json index c4598f93..f393653a 100644 --- a/packages/protoc-gen-pothos/package.json +++ b/packages/protoc-gen-pothos/package.json @@ -2,13 +2,7 @@ "name": "protoc-gen-pothos", "version": "0.6.2", "description": "Generate DSL for Pothos GraphQL from Protocol Buffers IDL", - "keywords": [ - "graphql", - "grpc", - "pothos", - "protobuf", - "typescript" - ], + "keywords": ["graphql", "grpc", "pothos", "protobuf", "typescript"], "bin": { "protoc-gen-pothos": "./bin/protoc-gen-pothos.cjs" }, @@ -31,13 +25,7 @@ } } }, - "files": [ - "bin/", - "src/", - "dist/", - "!src/**/*.test.ts", - "!src/**/__tests__/" - ], + "files": ["bin/", "src/", "dist/", "!src/**/*.test.ts", "!src/**/__tests__/"], "sideEffects": false, "private": false, "publishConfig": { diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts index 537d6d44..9c8dc75e 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts @@ -1,36 +1,48 @@ -import { getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; -import { +import { createFileRegistry } from "@bufbuild/protobuf"; +import { EnumType, - TypeOptions, + type TypeOptions, + defaultScalarMapping, defaultScalarMappingForTsProto, - defaultScalarMapping } from "@proto-graphql/codegen-core"; -import { createFileRegistry } from "@bufbuild/protobuf"; +import { + type TestapisPackage, + getTestapisFileDescriptorSet, +} from "@proto-graphql/testapis-proto"; import { describe, expect, test } from "vitest"; import { createEnumTypeCode } from "./enumType.js"; import type { PothosPrinterOptions } from "./util.js"; -function generateEnumTypeCode(packageName: TestapisPackage, enumTypeNameInProto: string, options: PothosPrinterOptions): string { +function generateEnumTypeCode( + packageName: TestapisPackage, + enumTypeNameInProto: string, + options: PothosPrinterOptions, +): string { const typeOptions: TypeOptions = { partialInputs: false, - scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + scalarMapping: + options.protobuf === "ts-proto" + ? defaultScalarMappingForTsProto + : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; const descSet = getTestapisFileDescriptorSet(packageName); const registry = createFileRegistry(descSet); - + // The actual proto package might differ from the TestapisPackage key // For example: "testapis.enums" key but "testapi.enums" proto package let descEnum = registry.getEnum(`${packageName}.${enumTypeNameInProto}`); - + if (descEnum === undefined && packageName === "testapis.enums") { // Try with the actual proto package name descEnum = registry.getEnum(`testapi.enums.${enumTypeNameInProto}`); } - + if (descEnum === undefined) { - throw new Error(`Enum ${enumTypeNameInProto} not found in package ${packageName}`); + throw new Error( + `Enum ${enumTypeNameInProto} not found in package ${packageName}`, + ); } const enumType = new EnumType(descEnum, typeOptions); @@ -60,17 +72,29 @@ describe("createEnumTypeCode", () => { }); test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified", options); + const code = generateEnumTypeCode( + "testapis.enums", + "MyEnumWithoutUnspecified", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessage.NestedEnum", options); + const code = generateEnumTypeCode( + "testapis.nested", + "ParentMessage.NestedEnum", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for enum with extensions", () => { - const code = generateEnumTypeCode("testapis.extensions", "PrefixedEnum", options); + const code = generateEnumTypeCode( + "testapis.extensions", + "PrefixedEnum", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -94,13 +118,21 @@ describe("createEnumTypeCode", () => { }); test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnumWithoutUnspecified", options); + const code = generateEnumTypeCode( + "testapis.enums", + "MyEnumWithoutUnspecified", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for nested enum", () => { - const code = generateEnumTypeCode("testapis.nested", "ParentMessage.NestedEnum", options); + const code = generateEnumTypeCode( + "testapis.nested", + "ParentMessage.NestedEnum", + options, + ); expect(code).toMatchSnapshot(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts index f5c364e6..5526d924 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts @@ -1,37 +1,49 @@ -import { getTestapisFileDescriptorSet, TestapisPackage, buildCodeGeneratorRequest } from "@proto-graphql/testapis-proto"; +import { createFileRegistry } from "@bufbuild/protobuf"; import { InputObjectType, - ObjectType, - TypeOptions, - defaultScalarMappingForTsProto, + type TypeOptions, defaultScalarMapping, - collectTypesFromFile, - createRegistryFromSchema + defaultScalarMappingForTsProto, } from "@proto-graphql/codegen-core"; -import { createFileRegistry } from "@bufbuild/protobuf"; -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { + type TestapisPackage, + getTestapisFileDescriptorSet, +} from "@proto-graphql/testapis-proto"; import { describe, expect, test } from "vitest"; import { createInputObjectTypeCode } from "./inputObjectType.js"; import type { PothosPrinterOptions } from "./util.js"; -function generateInputObjectTypeCode(packageName: TestapisPackage, typeNameInProto: string, options: PothosPrinterOptions, partialInputs = false): string { +function generateInputObjectTypeCode( + packageName: TestapisPackage, + typeNameInProto: string, + options: PothosPrinterOptions, + partialInputs = false, +): string { const typeOptions: TypeOptions = { partialInputs, - scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + scalarMapping: + options.protobuf === "ts-proto" + ? defaultScalarMappingForTsProto + : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet) + const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { - throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + throw new Error( + `Message ${typeNameInProto} not found in package ${packageName}`, + ); } const inputType = new InputObjectType(descMsg, typeOptions); - const code = createInputObjectTypeCode(partialInputs ? inputType.toPartialInput() : inputType, registry, options) + const code = createInputObjectTypeCode( + partialInputs ? inputType.toPartialInput() : inputType, + registry, + options, + ); return code.toString(); } @@ -51,27 +63,47 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Primitives", options); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Primitives", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Message", options); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Message", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode("testapis.oneof", "OneofParent", options); + const code = generateInputObjectTypeCode( + "testapis.oneof", + "OneofParent", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessage", options); + const code = generateInputObjectTypeCode( + "testapis.empty_types", + "EmptyMessage", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for nested input types", () => { - const code = generateInputObjectTypeCode("testapis.nested", "ParentMessage", options); + const code = generateInputObjectTypeCode( + "testapis.nested", + "ParentMessage", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -90,22 +122,38 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Primitives", options); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Primitives", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Message", options); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Message", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode("testapis.oneof", "OneofParent", options); + const code = generateInputObjectTypeCode( + "testapis.oneof", + "OneofParent", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode("testapis.empty_types", "EmptyMessage", options); + const code = generateInputObjectTypeCode( + "testapis.empty_types", + "EmptyMessage", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -124,7 +172,12 @@ describe("createInputObjectTypeCode", () => { }; test("generates code for partial input types", () => { - const code = generateInputObjectTypeCode("testapis.primitives", "Message", options, true); + const code = generateInputObjectTypeCode( + "testapis.primitives", + "Message", + options, + true, + ); expect(code).toMatchSnapshot(); }); }); diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts index 062101fe..04ebb69a 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts @@ -1,39 +1,46 @@ -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest, getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; +import { createFileRegistry } from "@bufbuild/protobuf"; import { ObjectType, - collectTypesFromFile, - createRegistryFromSchema, - defaultScalarMappingForTsProto, + type TypeOptions, defaultScalarMapping, - TypeOptions + defaultScalarMappingForTsProto, } from "@proto-graphql/codegen-core"; -import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; -import { code } from "ts-poet"; +import { + type TestapisPackage, + getTestapisFileDescriptorSet, +} from "@proto-graphql/testapis-proto"; import { describe, expect, test } from "vitest"; import { createObjectTypeCode } from "./objectType.js"; import type { PothosPrinterOptions } from "./util.js"; -import { createFileRegistry, createRegistry } from "@bufbuild/protobuf"; -function generateObjectTypeCode(packageName: TestapisPackage, messageTypeName: string, options: PothosPrinterOptions): string { +function generateObjectTypeCode( + packageName: TestapisPackage, + messageTypeName: string, + options: PothosPrinterOptions, +): string { const typeOptions: TypeOptions = { partialInputs: false, - scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + scalarMapping: + options.protobuf === "ts-proto" + ? defaultScalarMappingForTsProto + : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet) + const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${messageTypeName}`); if (descMsg === undefined) { - throw new Error(`Message ${messageTypeName} not found in package ${packageName}`); + throw new Error( + `Message ${messageTypeName} not found in package ${packageName}`, + ); } const objType = new ObjectType(descMsg, typeOptions); - const code = createObjectTypeCode(objType, registry, options) + const code = createObjectTypeCode(objType, registry, options); - return code.toString() + return code.toString(); } describe("createObjectTypeCode", () => { @@ -51,27 +58,47 @@ describe("createObjectTypeCode", () => { }; test("generates code for a simple message", () => { - const code = generateObjectTypeCode("testapis.primitives", "Primitives", options); + const code = generateObjectTypeCode( + "testapis.primitives", + "Primitives", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a message with nested fields", () => { - const code = generateObjectTypeCode("testapis.primitives", "Message", options); + const code = generateObjectTypeCode( + "testapis.primitives", + "Message", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a message with oneofs", () => { - const code = generateObjectTypeCode("testapis.oneof", "OneofParent", options); + const code = generateObjectTypeCode( + "testapis.oneof", + "OneofParent", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for empty message", () => { - const code = generateObjectTypeCode("testapis.empty_types", "EmptyMessage", options); + const code = generateObjectTypeCode( + "testapis.empty_types", + "EmptyMessage", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for nested types", () => { - const code = generateObjectTypeCode("testapis.nested", "ParentMessage", options); + const code = generateObjectTypeCode( + "testapis.nested", + "ParentMessage", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -90,17 +117,29 @@ describe("createObjectTypeCode", () => { }; test("generates code for a simple message", () => { - const code = generateObjectTypeCode("testapis.primitives", "Primitives", options); + const code = generateObjectTypeCode( + "testapis.primitives", + "Primitives", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a message with nested fields", () => { - const code = generateObjectTypeCode("testapis.primitives", "Message", options); + const code = generateObjectTypeCode( + "testapis.primitives", + "Message", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a message with oneofs", () => { - const code = generateObjectTypeCode("testapis.oneof", "OneofParent", options); + const code = generateObjectTypeCode( + "testapis.oneof", + "OneofParent", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -119,7 +158,11 @@ describe("createObjectTypeCode", () => { }; test("generates code for message with field extensions", () => { - const code = generateObjectTypeCode("testapis.extensions", "PrefixedMessage", options); + const code = generateObjectTypeCode( + "testapis.extensions", + "PrefixedMessage", + options, + ); expect(code).toMatchSnapshot(); }); }); diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts index bd72a84c..07c9d231 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts @@ -1,63 +1,91 @@ -import { createEcmaScriptPlugin } from "@bufbuild/protoplugin"; -import { buildCodeGeneratorRequest, getTestapisFileDescriptorSet, TestapisPackage } from "@proto-graphql/testapis-proto"; +import { createFileRegistry } from "@bufbuild/protobuf"; import { OneofUnionType, SquashedOneofUnionType, - TypeOptions, - collectTypesFromFile, - createRegistryFromSchema, + type TypeOptions, + defaultScalarMapping, defaultScalarMappingForTsProto, - defaultScalarMapping } from "@proto-graphql/codegen-core"; -import { createTsGenerator, parsePothosOptions } from "@proto-graphql/protoc-plugin-helpers"; +import { + type TestapisPackage, + getTestapisFileDescriptorSet, +} from "@proto-graphql/testapis-proto"; import { describe, expect, test } from "vitest"; import { createOneofUnionTypeCode } from "./oneofUnionType.js"; import type { PothosPrinterOptions } from "./util.js"; -import { createFileRegistry } from "@bufbuild/protobuf"; -function generateOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProto: string, oneofFieldName: string, options: PothosPrinterOptions): string { +function generateOneofUnionTypeCode( + packageName: TestapisPackage, + typeNameInProto: string, + oneofFieldName: string, + options: PothosPrinterOptions, +): string { const typeOptions: TypeOptions = { partialInputs: false, - scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + scalarMapping: + options.protobuf === "ts-proto" + ? defaultScalarMappingForTsProto + : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet) + const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { - throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + throw new Error( + `Message ${typeNameInProto} not found in package ${packageName}`, + ); } - const descOneof = descMsg.oneofs.find(d => d.name === oneofFieldName) + const descOneof = descMsg.oneofs.find((d) => d.name === oneofFieldName); if (descOneof === undefined) { - throw new Error(`Oneof field ${oneofFieldName} not found in message ${typeNameInProto} in package ${packageName}`); + throw new Error( + `Oneof field ${oneofFieldName} not found in message ${typeNameInProto} in package ${packageName}`, + ); } const oneofType = new OneofUnionType(descOneof, typeOptions); - const code = createOneofUnionTypeCode(oneofType, registry, options).toString(); + const code = createOneofUnionTypeCode( + oneofType, + registry, + options, + ).toString(); return code.toString(); } -function generateSquashedOneofUnionTypeCode(packageName: TestapisPackage, typeNameInProto: string, options: PothosPrinterOptions): string { +function generateSquashedOneofUnionTypeCode( + packageName: TestapisPackage, + typeNameInProto: string, + options: PothosPrinterOptions, +): string { const typeOptions: TypeOptions = { partialInputs: false, - scalarMapping: options.protobuf === "ts-proto" ? defaultScalarMappingForTsProto : defaultScalarMapping, + scalarMapping: + options.protobuf === "ts-proto" + ? defaultScalarMappingForTsProto + : defaultScalarMapping, ignoreNonMessageOneofFields: false, }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet) + const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { - throw new Error(`Message ${typeNameInProto} not found in package ${packageName}`); + throw new Error( + `Message ${typeNameInProto} not found in package ${packageName}`, + ); } const oneofType = new SquashedOneofUnionType(descMsg, typeOptions); - const code = createOneofUnionTypeCode(oneofType, registry, options).toString(); + const code = createOneofUnionTypeCode( + oneofType, + registry, + options, + ).toString(); return code.toString(); } @@ -77,22 +105,41 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code for a required oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "required_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an optional oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "optional_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "optional_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a squashed oneof union", () => { - const code = generateSquashedOneofUnionTypeCode("testapis.extensions", "PrefixedMessage.SquashedMessage", options); + const code = generateSquashedOneofUnionTypeCode( + "testapis.extensions", + "PrefixedMessage.SquashedMessage", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for imported oneof member", () => { - const code = generateOneofUnionTypeCode("testapis.edgecases.import_oneof_member_from_other_file", "OneofParent", "oneof_field", options); + const code = generateOneofUnionTypeCode( + "testapis.edgecases.import_oneof_member_from_other_file", + "OneofParent", + "oneof_field", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -111,17 +158,31 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code for a required oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "required_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for an optional oneof union", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "optional_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "optional_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); test("generates code for a squashed oneof union", () => { - const code = generateSquashedOneofUnionTypeCode("testapis.extensions", "PrefixedMessage.SquashedMessage", options); + const code = generateSquashedOneofUnionTypeCode( + "testapis.extensions", + "PrefixedMessage.SquashedMessage", + options, + ); expect(code).toMatchSnapshot(); }); }); @@ -140,7 +201,12 @@ describe("createOneofUnionTypeCode", () => { }; test("generates code with correct imports for graphql_type layout", () => { - const code = generateOneofUnionTypeCode("testapis.oneof", "OneofParent", "required_oneof_members", options); + const code = generateOneofUnionTypeCode( + "testapis.oneof", + "OneofParent", + "required_oneof_members", + options, + ); expect(code).toMatchSnapshot(); }); }); From 3ea97b52b8cf029b628ab121a8fa0d90b20bf711 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 12:47:02 +0900 Subject: [PATCH 25/27] revert unrelated changes --- CLAUDE.md | 87 ---------------------------------------------------- package.json | 2 +- 2 files changed, 1 insertion(+), 88 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 9c028a71..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,87 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -This is proto-graphql-js, a Protobuf-First GraphQL Schema generator for JavaScript/TypeScript. It provides protoc plugins that generate GraphQL schema code from Protocol Buffer definitions. - -### Supported GraphQL Libraries -- **Pothos**: via `protoc-gen-pothos` package -- **Nexus**: via `protoc-gen-nexus` package - -### Supported Protobuf Libraries -- google-protobuf -- protobufjs -- ts-proto -- @bufbuild/protobuf (protobuf-es) - -## Essential Commands - -```bash -# Install dependencies -pnpm install - -# Build all packages -pnpm build - -# Run unit tests -pnpm test - -# Run E2E tests (generates test APIs first) -pnpm test:e2e - -# Lint and format code (auto-fix) -pnpm lint - -# Clean build artifacts -pnpm clean - -# Run a specific test file -pnpm vitest path/to/test.spec.ts - -# Run tests in watch mode -pnpm vitest --watch -``` - -## Repository Structure - -This is a pnpm workspace monorepo with packages organized as: -- `/packages/` - Main packages (protoc-gen-pothos, protoc-gen-nexus, etc.) -- `/devPackages/` - Development utilities and test proto files -- `/e2e/` - End-to-end test suites with generated GraphQL schemas - -Build orchestration uses Turbo for caching and parallel execution. - -## Code Style and Conventions - -- **ESM-first** with CommonJS compatibility (type: "module" in package.json) -- **TypeScript** with strict typing -- **Biome** for linting and formatting -- **No default exports** (enforced by linter) -- **Organized imports** (auto-organized by Biome) -- **2-space indentation** - -## Testing Strategy - -- Unit tests use Vitest -- E2E tests generate GraphQL schemas from test proto files and verify output -- Test matrix covers combinations of GraphQL libraries × Protobuf implementations -- Snapshot testing for generated schema verification - -## Proto Compilation Workflow - -The project generates GraphQL schema code from .proto files: - -1. Proto files → protoc with custom plugin → GraphQL schema code -2. Plugins read proto descriptors and generate type-safe GraphQL definitions -3. Generated code integrates with Pothos or Nexus GraphQL libraries - -Key proto extensions are defined in `/proto-graphql/proto/graphql/schema.proto`. - -## Development Tips - -- When modifying protoc plugins, rebuild before testing: `pnpm build` -- E2E tests require proto compilation: `pnpm test:e2e:gen` before `pnpm test:e2e` -- Use `pnpm lint` to auto-fix formatting issues -- Generated files are in `__generated__` directories (ignored by linter) \ No newline at end of file diff --git a/package.json b/package.json index 9a6dc4ff..3ecbf30f 100644 --- a/package.json +++ b/package.json @@ -45,5 +45,5 @@ } }, "name": "proto-nexus", - "packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677" + "packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b" } From 4b656d4e4062a3a4fd046ab8f39ade7691affe9f Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 13:31:22 +0900 Subject: [PATCH 26/27] refactor: convert all printer tests to use parameterized tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Apply consistent parameterized test pattern across all printer tests - Use declarative test case structure with TestCase and TestSuite types - Replace repetitive test code with test.each() for better maintainability - Update all test snapshots to match new test names - Improve code organization and reduce duplication This refactoring makes it easier to add new test cases and maintain existing ones by using a single test implementation that iterates over test configurations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../__snapshots__/enumType.test.ts.snap | 14 +- .../inputObjectType.test.ts.snap | 20 +- .../__snapshots__/objectType.test.ts.snap | 12 +- .../__snapshots__/oneofUnionType.test.ts.snap | 12 +- .../src/dslgen/printers/enumType.test.ts | 132 ++++++---- .../dslgen/printers/inputObjectType.test.ts | 226 +++++++++-------- .../src/dslgen/printers/objectType.test.ts | 160 ++++++------ .../dslgen/printers/oneofUnionType.test.ts | 185 ++++++++------ .../__snapshots__/enumType.test.ts.snap | 14 +- .../inputObjectType.test.ts.snap | 20 +- .../__snapshots__/objectType.test.ts.snap | 18 +- .../__snapshots__/oneofUnionType.test.ts.snap | 16 +- .../src/dslgen/printers/enumType.test.ts | 156 +++++++----- .../dslgen/printers/inputObjectType.test.ts | 228 ++++++++++-------- .../src/dslgen/printers/objectType.test.ts | 208 ++++++++-------- .../dslgen/printers/oneofUnionType.test.ts | 227 ++++++++++------- 16 files changed, 937 insertions(+), 711 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/enumType.test.ts.snap b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/enumType.test.ts.snap index 4881e999..629de3ad 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/enumType.test.ts.snap +++ b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/enumType.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createEnumTypeCode > google-protobuf > generates code for a simple enum 1`] = ` +exports[`createEnumTypeCode > google-protobuf > 'generates code for a simple enum' 1`] = ` "import { enumType } from "nexus"; export const MyEnum = enumType({ @@ -18,7 +18,7 @@ export const MyEnum = enumType({ " `; -exports[`createEnumTypeCode > google-protobuf > generates code for an enum without unspecified 1`] = ` +exports[`createEnumTypeCode > google-protobuf > 'generates code for an enum without un…' 1`] = ` "import { enumType } from "nexus"; export const MyEnumWithoutUnspecified = enumType({ @@ -47,7 +47,7 @@ export const MyEnumWithoutUnspecified = enumType({ " `; -exports[`createEnumTypeCode > google-protobuf > generates code for enum with extensions 1`] = ` +exports[`createEnumTypeCode > google-protobuf > 'generates code for enum with extensio…' 1`] = ` "import { enumType } from "nexus"; export const TestPrefixPrefixedEnum = enumType({ @@ -67,7 +67,7 @@ export const TestPrefixPrefixedEnum = enumType({ " `; -exports[`createEnumTypeCode > google-protobuf > generates code for nested enum 1`] = ` +exports[`createEnumTypeCode > google-protobuf > 'generates code for nested enum' 1`] = ` "import { enumType } from "nexus"; export const ParentMessageNestedEnum = enumType({ @@ -88,7 +88,7 @@ export const ParentMessageNestedEnum = enumType({ " `; -exports[`createEnumTypeCode > protobufjs > generates code for a simple enum 1`] = ` +exports[`createEnumTypeCode > protobufjs > 'generates code for a simple enum' 1`] = ` "import { enumType } from "nexus"; export const MyEnum = enumType({ @@ -106,7 +106,7 @@ export const MyEnum = enumType({ " `; -exports[`createEnumTypeCode > protobufjs > generates code for an enum without unspecified 1`] = ` +exports[`createEnumTypeCode > protobufjs > 'generates code for an enum without un…' 1`] = ` "import { enumType } from "nexus"; export const MyEnumWithoutUnspecified = enumType({ @@ -135,7 +135,7 @@ export const MyEnumWithoutUnspecified = enumType({ " `; -exports[`createEnumTypeCode > protobufjs > generates code for nested enum 1`] = ` +exports[`createEnumTypeCode > protobufjs > 'generates code for nested enum' 1`] = ` "import { enumType } from "nexus"; export const ParentMessageNestedEnum = enumType({ diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap index 4d25938f..b392988c 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap +++ b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createInputObjectTypeCode > google-protobuf > generates code for a simple input object 1`] = ` +exports[`createInputObjectTypeCode > google-protobuf > 'generates code for a simple input obj…' 1`] = ` "import { Primitives } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; import { inputObjectType, list, nonNull } from "nexus"; import { stringToNumber } from "proto-nexus"; @@ -302,7 +302,7 @@ export const PrimitivesInput = Object.assign( " `; -exports[`createInputObjectTypeCode > google-protobuf > generates code for an input object with nested fields 1`] = ` +exports[`createInputObjectTypeCode > google-protobuf > 'generates code for an input object wi…' 1`] = ` "import { Message } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; import { inputObjectType, list, nonNull, nullable } from "nexus"; @@ -393,7 +393,7 @@ export const MessageInput = Object.assign( " `; -exports[`createInputObjectTypeCode > google-protobuf > generates code for an input object with oneof fields 1`] = ` +exports[`createInputObjectTypeCode > google-protobuf > 'generates code for an input object wi…' 2`] = ` "import { OneofParent } from "@testapis/google-protobuf/testapis/oneof/oneof_pb"; import { inputObjectType, nonNull, nullable } from "nexus"; @@ -493,7 +493,7 @@ export const OneofParentInput = Object.assign( " `; -exports[`createInputObjectTypeCode > google-protobuf > generates code for empty input object 1`] = ` +exports[`createInputObjectTypeCode > google-protobuf > 'generates code for empty input object' 1`] = ` "import { EmptyMessage } from "@testapis/google-protobuf/testapis/empty_types/empty_pb"; import { inputObjectType } from "nexus"; @@ -523,7 +523,7 @@ export const EmptyMessageInput = Object.assign( " `; -exports[`createInputObjectTypeCode > google-protobuf > generates code for nested input types 1`] = ` +exports[`createInputObjectTypeCode > google-protobuf > 'generates code for nested input types' 1`] = ` "import { ParentMessage } from "@testapis/google-protobuf/testapis/nested/nested_pb"; import { inputObjectType, nonNull, nullable } from "nexus"; @@ -593,7 +593,7 @@ export const ParentMessageInput = Object.assign( " `; -exports[`createInputObjectTypeCode > protobufjs > generates code for a simple input object 1`] = ` +exports[`createInputObjectTypeCode > protobufjs > 'generates code for a simple input obj…' 1`] = ` "import { testapis } from "@testapis/protobufjs/testapis/primitives"; import { inputObjectType, list, nonNull } from "nexus"; import { stringToNumber } from "proto-nexus"; @@ -895,7 +895,7 @@ export const PrimitivesInput = Object.assign( " `; -exports[`createInputObjectTypeCode > protobufjs > generates code for an input object with nested fields 1`] = ` +exports[`createInputObjectTypeCode > protobufjs > 'generates code for an input object wi…' 1`] = ` "import { testapis } from "@testapis/protobufjs/testapis/primitives"; import { inputObjectType, list, nonNull, nullable } from "nexus"; @@ -986,7 +986,7 @@ export const MessageInput = Object.assign( " `; -exports[`createInputObjectTypeCode > protobufjs > generates code for an input object with oneof fields 1`] = ` +exports[`createInputObjectTypeCode > protobufjs > 'generates code for an input object wi…' 2`] = ` "import { testapis } from "@testapis/protobufjs/testapis/oneof"; import { inputObjectType, nonNull, nullable } from "nexus"; @@ -1086,7 +1086,7 @@ export const OneofParentInput = Object.assign( " `; -exports[`createInputObjectTypeCode > protobufjs > generates code for empty input object 1`] = ` +exports[`createInputObjectTypeCode > protobufjs > 'generates code for empty input object' 1`] = ` "import { testapis } from "@testapis/protobufjs/testapis/empty_types"; import { inputObjectType } from "nexus"; @@ -1116,7 +1116,7 @@ export const EmptyMessageInput = Object.assign( " `; -exports[`createInputObjectTypeCode > with partial inputs > generates code for partial input types 1`] = ` +exports[`createInputObjectTypeCode > with partial inputs > 'generates code for partial input types' 1`] = ` "import { Message } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; import { inputObjectType, list, nonNull, nullable } from "nexus"; diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/objectType.test.ts.snap b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/objectType.test.ts.snap index d120a355..dbfcb752 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/objectType.test.ts.snap +++ b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/objectType.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createObjectTypeCode > google-protobuf > generates code for a message with nested fields 1`] = ` +exports[`createObjectTypeCode > google-protobuf > 'generates code for a message with nes…' 1`] = ` "import { Message as Message1 } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; import { list, nonNull, nullable, objectType } from "nexus"; @@ -76,7 +76,7 @@ export const Message = objectType({ " `; -exports[`createObjectTypeCode > google-protobuf > generates code for a simple message 1`] = ` +exports[`createObjectTypeCode > google-protobuf > 'generates code for a simple message' 1`] = ` "import { Primitives as Primitives1 } from "@testapis/google-protobuf/testapis/primitives/primitives_pb"; import { list, nonNull, objectType } from "nexus"; @@ -355,7 +355,7 @@ export const Primitives = objectType({ " `; -exports[`createObjectTypeCode > protobufjs > generates code for a message with nested fields 1`] = ` +exports[`createObjectTypeCode > protobufjs > 'generates code for a message with nes…' 1`] = ` "import { testapis } from "@testapis/protobufjs/testapis/primitives"; import { list, nonNull, nullable, objectType } from "nexus"; @@ -431,7 +431,7 @@ export const Message = objectType({ " `; -exports[`createObjectTypeCode > protobufjs > generates code for a message with oneofs 1`] = ` +exports[`createObjectTypeCode > protobufjs > 'generates code for a message with one…' 1`] = ` "import { testapis } from "@testapis/protobufjs/testapis/oneof"; import { nonNull, nullable, objectType } from "nexus"; @@ -491,7 +491,7 @@ export const OneofParent = objectType({ " `; -exports[`createObjectTypeCode > protobufjs > generates code for a simple message 1`] = ` +exports[`createObjectTypeCode > protobufjs > 'generates code for a simple message' 1`] = ` "import { testapis } from "@testapis/protobufjs/testapis/primitives"; import { list, nonNull, objectType } from "nexus"; @@ -815,7 +815,7 @@ export const Primitives = objectType({ " `; -exports[`createObjectTypeCode > with extensions > generates code for message with field extensions 1`] = ` +exports[`createObjectTypeCode > with extensions > 'generates code for message with field…' 1`] = ` "import { EnumWillRename, PrefixedEnum, diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap index b9069495..b8f1b63e 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap +++ b/packages/protoc-gen-nexus/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createOneofUnionTypeCode > google-protobuf > generates code for a simple oneof union 1`] = ` +exports[`createOneofUnionTypeCode > google-protobuf > 'generates code for a simple oneof uni…' 1`] = ` "import { unionType } from "nexus"; export const OneofParentRequiredOneofMembers = unionType({ @@ -25,7 +25,7 @@ export const OneofParentRequiredOneofMembers = unionType({ " `; -exports[`createOneofUnionTypeCode > google-protobuf > generates code for import squashed union 1`] = ` +exports[`createOneofUnionTypeCode > google-protobuf > 'generates code for import squashed un…' 1`] = ` "import { unionType } from "nexus"; export const SquashedOneof = unionType({ @@ -49,7 +49,7 @@ export const SquashedOneof = unionType({ " `; -exports[`createOneofUnionTypeCode > google-protobuf > generates code for optional oneof union 1`] = ` +exports[`createOneofUnionTypeCode > google-protobuf > 'generates code for optional oneof uni…' 1`] = ` "import { unionType } from "nexus"; export const OneofParentOptionalOneofMembers = unionType({ @@ -73,7 +73,7 @@ export const OneofParentOptionalOneofMembers = unionType({ " `; -exports[`createOneofUnionTypeCode > protobufjs > generates code for a simple oneof union 1`] = ` +exports[`createOneofUnionTypeCode > protobufjs > 'generates code for a simple oneof uni…' 1`] = ` "import { unionType } from "nexus"; export const OneofParentRequiredOneofMembers = unionType({ @@ -98,7 +98,7 @@ export const OneofParentRequiredOneofMembers = unionType({ " `; -exports[`createOneofUnionTypeCode > protobufjs > generates code for import squashed union 1`] = ` +exports[`createOneofUnionTypeCode > protobufjs > 'generates code for import squashed un…' 1`] = ` "import { unionType } from "nexus"; export const SquashedOneof = unionType({ @@ -122,7 +122,7 @@ export const SquashedOneof = unionType({ " `; -exports[`createOneofUnionTypeCode > protobufjs > generates code for optional oneof union 1`] = ` +exports[`createOneofUnionTypeCode > protobufjs > 'generates code for optional oneof uni…' 1`] = ` "import { unionType } from "nexus"; export const OneofParentOptionalOneofMembers = unionType({ diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts index 6203b6cb..cdb66f1a 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts @@ -46,55 +46,91 @@ function generateEnumTypeCode( return code.toString(); } -describe("createEnumTypeCode", () => { - describe("google-protobuf", () => { - test("generates code for a simple enum", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnum"); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode( - "testapis.enums", - "MyEnumWithoutUnspecified", - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for nested enum", () => { - const code = generateEnumTypeCode( - "testapis.nested", - "ParentMessage.NestedEnum", - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for enum with extensions", () => { - const code = generateEnumTypeCode("testapis.extensions", "PrefixedEnum"); - expect(code).toMatchSnapshot(); - }); - }); +type TestCase = { + test: string; + args: { + packageName: TestapisPackage; + enumTypeNameInProto: string; + }; +}; - describe("protobufjs", () => { - test("generates code for a simple enum", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnum"); - expect(code).toMatchSnapshot(); - }); +type TestSuite = { + suite: string; + cases: TestCase[]; +}; - test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode( - "testapis.enums", - "MyEnumWithoutUnspecified", - ); - expect(code).toMatchSnapshot(); - }); +const testSuites: TestSuite[] = [ + { + suite: "google-protobuf", + cases: [ + { + test: "generates code for a simple enum", + args: { + packageName: "testapis.enums", + enumTypeNameInProto: "MyEnum", + }, + }, + { + test: "generates code for an enum without unspecified", + args: { + packageName: "testapis.enums", + enumTypeNameInProto: "MyEnumWithoutUnspecified", + }, + }, + { + test: "generates code for nested enum", + args: { + packageName: "testapis.nested", + enumTypeNameInProto: "ParentMessage.NestedEnum", + }, + }, + { + test: "generates code for enum with extensions", + args: { + packageName: "testapis.extensions", + enumTypeNameInProto: "PrefixedEnum", + }, + }, + ], + }, + { + suite: "protobufjs", + cases: [ + { + test: "generates code for a simple enum", + args: { + packageName: "testapis.enums", + enumTypeNameInProto: "MyEnum", + }, + }, + { + test: "generates code for an enum without unspecified", + args: { + packageName: "testapis.enums", + enumTypeNameInProto: "MyEnumWithoutUnspecified", + }, + }, + { + test: "generates code for nested enum", + args: { + packageName: "testapis.nested", + enumTypeNameInProto: "ParentMessage.NestedEnum", + }, + }, + ], + }, +]; - test("generates code for nested enum", () => { - const code = generateEnumTypeCode( - "testapis.nested", - "ParentMessage.NestedEnum", - ); - expect(code).toMatchSnapshot(); +describe("createEnumTypeCode", () => { + for (const { suite, cases } of testSuites) { + describe(suite, () => { + test.each(cases)("$test", ({ args }) => { + const code = generateEnumTypeCode( + args.packageName, + args.enumTypeNameInProto, + ); + expect(code).toMatchSnapshot(); + }); }); - }); -}); + } +}); \ No newline at end of file diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts index c4ee282f..ead080d1 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/inputObjectType.test.ts @@ -44,128 +44,146 @@ function generateInputObjectTypeCode( return code.toString(); } -describe("createInputObjectTypeCode", () => { - describe("google-protobuf", () => { - const options: NexusPrinterOptions = { +type TestCase = { + test: string; + args: { + packageName: TestapisPackage; + typeNameInProto: string; + partialInputs?: boolean; + }; +}; + +type TestSuite = { + suite: string; + options: NexusPrinterOptions; + cases: TestCase[]; +}; + +const testSuites: TestSuite[] = [ + { + suite: "google-protobuf", + options: { dsl: "nexus", protobuf: "google-protobuf" as const, importPrefix: "@testapis/google-protobuf", emitImportedFiles: false, fileLayout: "proto_file", filenameSuffix: ".nexus", - }; - - test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Primitives", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Message", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode( - "testapis.oneof", - "OneofParent", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode( - "testapis.empty_types", - "EmptyMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for nested input types", () => { - const code = generateInputObjectTypeCode( - "testapis.nested", - "ParentMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("protobufjs", () => { - const options: NexusPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple input object", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Primitives", + }, + }, + { + test: "generates code for an input object with nested fields", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Message", + }, + }, + { + test: "generates code for an input object with oneof fields", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + }, + }, + { + test: "generates code for empty input object", + args: { + packageName: "testapis.empty_types", + typeNameInProto: "EmptyMessage", + }, + }, + { + test: "generates code for nested input types", + args: { + packageName: "testapis.nested", + typeNameInProto: "ParentMessage", + }, + }, + ], + }, + { + suite: "protobufjs", + options: { dsl: "nexus", protobuf: "protobufjs" as const, importPrefix: "@testapis/protobufjs", emitImportedFiles: false, fileLayout: "proto_file", filenameSuffix: ".nexus", - }; - - test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Primitives", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Message", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode( - "testapis.oneof", - "OneofParent", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode( - "testapis.empty_types", - "EmptyMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("with partial inputs", () => { - const options: NexusPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple input object", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Primitives", + }, + }, + { + test: "generates code for an input object with nested fields", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Message", + }, + }, + { + test: "generates code for an input object with oneof fields", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + }, + }, + { + test: "generates code for empty input object", + args: { + packageName: "testapis.empty_types", + typeNameInProto: "EmptyMessage", + }, + }, + ], + }, + { + suite: "with partial inputs", + options: { dsl: "nexus", protobuf: "google-protobuf" as const, importPrefix: "@testapis/google-protobuf", emitImportedFiles: false, fileLayout: "proto_file", filenameSuffix: ".nexus", - }; + }, + cases: [ + { + test: "generates code for partial input types", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Message", + partialInputs: true, + }, + }, + ], + }, +]; - test("generates code for partial input types", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Message", - options, - true, - ); - expect(code).toMatchSnapshot(); +describe("createInputObjectTypeCode", () => { + for (const { suite, options, cases } of testSuites) { + describe(suite, () => { + test.each(cases)("$test", ({ args }) => { + const code = generateInputObjectTypeCode( + args.packageName, + args.typeNameInProto, + options, + args.partialInputs ?? false, + ); + expect(code).toMatchSnapshot(); + }); }); - }); + } }); diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts index 89ba2362..c269bf04 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts @@ -39,91 +39,115 @@ function generateObjectTypeCode( return code.toString(); } -describe("createObjectTypeCode", () => { - describe("google-protobuf", () => { - const options: NexusPrinterOptions = { +type TestCase = { + test: string; + args: { + packageName: TestapisPackage; + messageTypeName: string; + }; +}; + +type TestSuite = { + suite: string; + options: NexusPrinterOptions; + cases: TestCase[]; +}; + +const testSuites: TestSuite[] = [ + { + suite: "google-protobuf", + options: { dsl: "nexus", protobuf: "google-protobuf" as const, importPrefix: "@testapis/google-protobuf", emitImportedFiles: false, fileLayout: "proto_file", filenameSuffix: ".nexus", - }; - - test("generates code for a simple message", () => { - const code = generateObjectTypeCode( - "testapis.primitives", - "Primitives", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a message with nested fields", () => { - const code = generateObjectTypeCode( - "testapis.primitives", - "Message", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("protobufjs", () => { - const options: NexusPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple message", + args: { + packageName: "testapis.primitives", + messageTypeName: "Primitives", + }, + }, + { + test: "generates code for a message with nested fields", + args: { + packageName: "testapis.primitives", + messageTypeName: "Message", + }, + }, + ], + }, + { + suite: "protobufjs", + options: { dsl: "nexus", protobuf: "protobufjs" as const, importPrefix: "@testapis/protobufjs", emitImportedFiles: false, fileLayout: "proto_file", filenameSuffix: ".nexus", - }; - - test("generates code for a simple message", () => { - const code = generateObjectTypeCode( - "testapis.primitives", - "Primitives", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a message with nested fields", () => { - const code = generateObjectTypeCode( - "testapis.primitives", - "Message", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a message with oneofs", () => { - const code = generateObjectTypeCode( - "testapis.oneof", - "OneofParent", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("with extensions", () => { - const options: NexusPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple message", + args: { + packageName: "testapis.primitives", + messageTypeName: "Primitives", + }, + }, + { + test: "generates code for a message with nested fields", + args: { + packageName: "testapis.primitives", + messageTypeName: "Message", + }, + }, + { + test: "generates code for a message with oneofs", + args: { + packageName: "testapis.oneof", + messageTypeName: "OneofParent", + }, + }, + ], + }, + { + suite: "with extensions", + options: { dsl: "nexus", protobuf: "google-protobuf" as const, importPrefix: "@testapis/google-protobuf", emitImportedFiles: false, fileLayout: "proto_file", filenameSuffix: ".nexus", - }; + }, + cases: [ + { + test: "generates code for message with field extensions", + args: { + packageName: "testapis.extensions", + messageTypeName: "PrefixedMessage", + }, + }, + ], + }, +]; - test("generates code for message with field extensions", () => { - const code = generateObjectTypeCode( - "testapis.extensions", - "PrefixedMessage", - options, - ); - expect(code).toMatchSnapshot(); +describe("createObjectTypeCode", () => { + for (const { suite, options, cases } of testSuites) { + describe(suite, () => { + test.each(cases)("$test", ({ args }) => { + const code = generateObjectTypeCode( + args.packageName, + args.messageTypeName, + options, + ); + expect(code).toMatchSnapshot(); + }); }); - }); -}); + } +}); \ No newline at end of file diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts index ef084cef..af828f75 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts @@ -1,9 +1,9 @@ import { createFileRegistry } from "@bufbuild/protobuf"; -import { +import { OneofUnionType, SquashedOneofUnionType, type TypeOptions, - defaultScalarMapping, + defaultScalarMapping } from "@proto-graphql/codegen-core"; import { type TestapisPackage, @@ -26,7 +26,7 @@ function generateOneofUnionTypeCode( }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet); + const registry = createFileRegistry(descSet) const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { throw new Error( @@ -64,7 +64,7 @@ function generateSquashedOneofUnionTypeCode( }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet); + const registry = createFileRegistry(descSet) const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { throw new Error( @@ -83,84 +83,127 @@ function generateSquashedOneofUnionTypeCode( return code.toString(); } -describe("createOneofUnionTypeCode", () => { - describe("google-protobuf", () => { - const options: NexusPrinterOptions = { +type OneofTestCase = { + test: string; + args: { + packageName: TestapisPackage; + typeNameInProto: string; + oneofFieldName: string; + }; +}; + +type SquashedTestCase = { + test: string; + args: { + packageName: TestapisPackage; + typeNameInProto: string; + }; +}; + +type TestCase = OneofTestCase | SquashedTestCase; + +type TestSuite = { + suite: string; + options: NexusPrinterOptions; + cases: TestCase[]; +}; + +const testSuites: TestSuite[] = [ + { + suite: "google-protobuf", + options: { dsl: "nexus", protobuf: "google-protobuf" as const, importPrefix: "@testapis/google-protobuf", emitImportedFiles: false, fileLayout: "proto_file", filenameSuffix: ".nexus", - }; - - test("generates code for a simple oneof union", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "required_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for optional oneof union", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "optional_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for import squashed union", () => { - const code = generateSquashedOneofUnionTypeCode( - "testapis.edgecases.import_squashed_union.pkg1", - "SquashedOneof", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("protobufjs", () => { - const options: NexusPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple oneof union", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "required_oneof_members", + }, + } as OneofTestCase, + { + test: "generates code for optional oneof union", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "optional_oneof_members", + }, + } as OneofTestCase, + { + test: "generates code for import squashed union", + args: { + packageName: "testapis.edgecases.import_squashed_union.pkg1", + typeNameInProto: "SquashedOneof", + }, + } as SquashedTestCase, + ], + }, + { + suite: "protobufjs", + options: { dsl: "nexus", protobuf: "protobufjs" as const, importPrefix: "@testapis/protobufjs", emitImportedFiles: false, fileLayout: "proto_file", filenameSuffix: ".nexus", - }; - - test("generates code for a simple oneof union", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "required_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); - }); + }, + cases: [ + { + test: "generates code for a simple oneof union", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "required_oneof_members", + }, + } as OneofTestCase, + { + test: "generates code for optional oneof union", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "optional_oneof_members", + }, + } as OneofTestCase, + { + test: "generates code for import squashed union", + args: { + packageName: "testapis.edgecases.import_squashed_union.pkg1", + typeNameInProto: "SquashedOneof", + }, + } as SquashedTestCase, + ], + }, +]; - test("generates code for optional oneof union", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "optional_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for import squashed union", () => { - const code = generateSquashedOneofUnionTypeCode( - "testapis.edgecases.import_squashed_union.pkg1", - "SquashedOneof", - options, - ); - expect(code).toMatchSnapshot(); +describe("createOneofUnionTypeCode", () => { + for (const { suite, options, cases } of testSuites) { + describe(suite, () => { + test.each(cases)("$test", ({ args }) => { + if ("oneofFieldName" in args) { + const code = generateOneofUnionTypeCode( + args.packageName, + args.typeNameInProto, + args.oneofFieldName, + options, + ); + expect(code).toMatchSnapshot(); + } else { + const code = generateSquashedOneofUnionTypeCode( + args.packageName, + args.typeNameInProto, + options, + ); + expect(code).toMatchSnapshot(); + } + }); }); - }); -}); + } +}); \ No newline at end of file diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/enumType.test.ts.snap b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/enumType.test.ts.snap index 2f94cffe..6ce346ba 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/enumType.test.ts.snap +++ b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/enumType.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createEnumTypeCode > protobuf-es > generates code for a simple enum 1`] = ` +exports[`createEnumTypeCode > protobuf-es > 'generates code for a simple enum' 1`] = ` "import { EnumRef } from "@pothos/core"; import { MyEnum } from "@testapis/protobuf-es/testapis/enums/enums_pb"; import { builder } from "../../../../builder"; @@ -22,7 +22,7 @@ export const MyEnum$Ref: EnumRef = builder.enumType("MyEnum", { " `; -exports[`createEnumTypeCode > protobuf-es > generates code for an enum without unspecified 1`] = ` +exports[`createEnumTypeCode > protobuf-es > 'generates code for an enum without un…' 1`] = ` "import { EnumRef } from "@pothos/core"; import { MyEnumWithoutUnspecified } from "@testapis/protobuf-es/testapis/enums/enums_pb"; import { builder } from "../../../../builder"; @@ -45,7 +45,7 @@ export const MyEnumWithoutUnspecified$Ref: EnumRef protobuf-es > generates code for nested enum 1`] = ` +exports[`createEnumTypeCode > protobuf-es > 'generates code for nested enum' 1`] = ` "import { EnumRef } from "@pothos/core"; import { ParentMessage_NestedEnum } from "@testapis/protobuf-es/testapis/nested/nested_pb"; import { builder } from "../../../../builder"; @@ -67,7 +67,7 @@ export const ParentMessageNestedEnum$Ref: EnumRef ts-proto > generates code for a simple enum 1`] = ` +exports[`createEnumTypeCode > ts-proto > 'generates code for a simple enum' 1`] = ` "import { EnumRef } from "@pothos/core"; import { MyEnum } from "@testapis/ts-proto/testapis/enums/enums"; import { builder } from "../../../../builder"; @@ -89,7 +89,7 @@ export const MyEnum$Ref: EnumRef = builder.enumType("MyEnum", { " `; -exports[`createEnumTypeCode > ts-proto > generates code for an enum without unspecified 1`] = ` +exports[`createEnumTypeCode > ts-proto > 'generates code for an enum without un…' 1`] = ` "import { EnumRef } from "@pothos/core"; import { MyEnumWithoutUnspecified } from "@testapis/ts-proto/testapis/enums/enums"; import { builder } from "../../../../builder"; @@ -112,7 +112,7 @@ export const MyEnumWithoutUnspecified$Ref: EnumRef ts-proto > generates code for enum with extensions 1`] = ` +exports[`createEnumTypeCode > ts-proto > 'generates code for enum with extensio…' 1`] = ` "import { EnumRef } from "@pothos/core"; import { PrefixedEnum } from "@testapis/ts-proto/testapis/extensions/extensions"; import { builder } from "../../../../builder"; @@ -136,7 +136,7 @@ export const TestPrefixPrefixedEnum$Ref: EnumRef = b " `; -exports[`createEnumTypeCode > ts-proto > generates code for nested enum 1`] = ` +exports[`createEnumTypeCode > ts-proto > 'generates code for nested enum' 1`] = ` "import { EnumRef } from "@pothos/core"; import { ParentMessage_NestedEnum } from "@testapis/ts-proto/testapis/nested/nested"; import { builder } from "../../../../builder"; diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap index 27b955fa..eb8fadbf 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap +++ b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/inputObjectType.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createInputObjectTypeCode > protobuf-es > generates code for a simple input object 1`] = ` +exports[`createInputObjectTypeCode > protobuf-es > 'generates code for a simple input obj…' 1`] = ` "import { InputObjectRef } from "@pothos/core"; import { Primitives } from "@testapis/protobuf-es/testapis/primitives/primitives_pb"; import { builder } from "../../../../builder"; @@ -239,7 +239,7 @@ export function PrimitivesInput$toProto(input: PrimitivesInput$Shape | null | un " `; -exports[`createInputObjectTypeCode > protobuf-es > generates code for an input object with nested fields 1`] = ` +exports[`createInputObjectTypeCode > protobuf-es > 'generates code for an input object wi…' 1`] = ` "import { InputObjectRef } from "@pothos/core"; import { Message } from "@testapis/protobuf-es/testapis/primitives/primitives_pb"; import { builder } from "../../../../builder"; @@ -307,7 +307,7 @@ export function MessageInput$toProto(input: MessageInput$Shape | null | undefine " `; -exports[`createInputObjectTypeCode > protobuf-es > generates code for an input object with oneof fields 1`] = ` +exports[`createInputObjectTypeCode > protobuf-es > 'generates code for an input object wi…' 2`] = ` "import { InputObjectRef } from "@pothos/core"; import { OneofParent } from "@testapis/protobuf-es/testapis/oneof/oneof_pb"; import { builder } from "../../../../builder"; @@ -381,7 +381,7 @@ export function OneofParentInput$toProto(input: OneofParentInput$Shape | null | " `; -exports[`createInputObjectTypeCode > protobuf-es > generates code for empty input object 1`] = ` +exports[`createInputObjectTypeCode > protobuf-es > 'generates code for empty input object' 1`] = ` "import { InputObjectRef } from "@pothos/core"; import { EmptyMessage } from "@testapis/protobuf-es/testapis/empty_types/empty_pb"; import { builder } from "../../../../builder"; @@ -407,7 +407,7 @@ export function EmptyMessageInput$toProto(input: EmptyMessageInput$Shape | null " `; -exports[`createInputObjectTypeCode > ts-proto > generates code for a simple input object 1`] = ` +exports[`createInputObjectTypeCode > ts-proto > 'generates code for a simple input obj…' 1`] = ` "import { InputObjectRef } from "@pothos/core"; import { Primitives } from "@testapis/ts-proto/testapis/primitives/primitives"; import { builder } from "../../../../builder"; @@ -611,7 +611,7 @@ export const PrimitivesInput$Ref: InputObjectRef = builde " `; -exports[`createInputObjectTypeCode > ts-proto > generates code for an input object with nested fields 1`] = ` +exports[`createInputObjectTypeCode > ts-proto > 'generates code for an input object wi…' 1`] = ` "import { InputObjectRef } from "@pothos/core"; import { builder } from "../../../../builder"; @@ -669,7 +669,7 @@ export const MessageInput$Ref: InputObjectRef = builder.inpu " `; -exports[`createInputObjectTypeCode > ts-proto > generates code for an input object with oneof fields 1`] = ` +exports[`createInputObjectTypeCode > ts-proto > 'generates code for an input object wi…' 2`] = ` "import { InputObjectRef } from "@pothos/core"; import { OneofParent } from "@testapis/ts-proto/testapis/oneof/oneof"; import { builder } from "../../../../builder"; @@ -727,7 +727,7 @@ export const OneofParentInput$Ref: InputObjectRef = buil " `; -exports[`createInputObjectTypeCode > ts-proto > generates code for empty input object 1`] = ` +exports[`createInputObjectTypeCode > ts-proto > 'generates code for empty input object' 1`] = ` "import { InputObjectRef } from "@pothos/core"; import { builder } from "../../../../builder"; @@ -748,7 +748,7 @@ export const EmptyMessageInput$Ref: InputObjectRef = bu " `; -exports[`createInputObjectTypeCode > ts-proto > generates code for nested input types 1`] = ` +exports[`createInputObjectTypeCode > ts-proto > 'generates code for nested input types' 1`] = ` "import { InputObjectRef } from "@pothos/core"; import { ParentMessage } from "@testapis/ts-proto/testapis/nested/nested"; import { builder } from "../../../../builder"; @@ -794,7 +794,7 @@ export const ParentMessageInput$Ref: InputObjectRef = " `; -exports[`createInputObjectTypeCode > with partial inputs > generates code for partial input types 1`] = ` +exports[`createInputObjectTypeCode > with partial inputs > 'generates code for partial input types' 1`] = ` "import { InputObjectRef } from "@pothos/core"; import { builder } from "../../../../builder"; diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/objectType.test.ts.snap b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/objectType.test.ts.snap index 6688a50b..fe291141 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/objectType.test.ts.snap +++ b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/objectType.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createObjectTypeCode > protobuf-es > generates code for a message with nested fields 1`] = ` +exports[`createObjectTypeCode > protobuf-es > 'generates code for a message with nes…' 1`] = ` "import { Message } from "@testapis/protobuf-es/testapis/primitives/primitives_pb"; import { builder } from "../../../../builder"; @@ -61,7 +61,7 @@ builder.objectType(Message$Ref, { " `; -exports[`createObjectTypeCode > protobuf-es > generates code for a message with oneofs 1`] = ` +exports[`createObjectTypeCode > protobuf-es > 'generates code for a message with one…' 1`] = ` "import { OneofParent } from "@testapis/protobuf-es/testapis/oneof/oneof_pb"; import { builder } from "../../../../builder"; @@ -106,7 +106,7 @@ builder.objectType(OneofParent$Ref, { " `; -exports[`createObjectTypeCode > protobuf-es > generates code for a simple message 1`] = ` +exports[`createObjectTypeCode > protobuf-es > 'generates code for a simple message' 1`] = ` "import { Primitives } from "@testapis/protobuf-es/testapis/primitives/primitives_pb"; import { builder } from "../../../../builder"; @@ -285,7 +285,7 @@ builder.objectType(Primitives$Ref, { " `; -exports[`createObjectTypeCode > ts-proto > generates code for a message with nested fields 1`] = ` +exports[`createObjectTypeCode > ts-proto > 'generates code for a message with nes…' 1`] = ` "import { Message } from "@testapis/ts-proto/testapis/primitives/primitives"; import { builder } from "../../../../builder"; @@ -346,7 +346,7 @@ builder.objectType(Message$Ref, { " `; -exports[`createObjectTypeCode > ts-proto > generates code for a message with oneofs 1`] = ` +exports[`createObjectTypeCode > ts-proto > 'generates code for a message with one…' 1`] = ` "import { OneofParent } from "@testapis/ts-proto/testapis/oneof/oneof"; import { builder } from "../../../../builder"; @@ -395,7 +395,7 @@ builder.objectType(OneofParent$Ref, { " `; -exports[`createObjectTypeCode > ts-proto > generates code for a simple message 1`] = ` +exports[`createObjectTypeCode > ts-proto > 'generates code for a simple message' 1`] = ` "import { Primitives } from "@testapis/ts-proto/testapis/primitives/primitives"; import { builder } from "../../../../builder"; @@ -574,7 +574,7 @@ builder.objectType(Primitives$Ref, { " `; -exports[`createObjectTypeCode > ts-proto > generates code for empty message 1`] = ` +exports[`createObjectTypeCode > ts-proto > 'generates code for empty message' 1`] = ` "import { EmptyMessage } from "@testapis/ts-proto/testapis/empty_types/empty"; import { builder } from "../../../../builder"; @@ -598,7 +598,7 @@ builder.objectType(EmptyMessage$Ref, { " `; -exports[`createObjectTypeCode > ts-proto > generates code for nested types 1`] = ` +exports[`createObjectTypeCode > ts-proto > 'generates code for nested types' 1`] = ` "import { ParentMessage, ParentMessage_NestedEnum } from "@testapis/ts-proto/testapis/nested/nested"; import { builder } from "../../../../builder"; @@ -647,7 +647,7 @@ builder.objectType(ParentMessage$Ref, { " `; -exports[`createObjectTypeCode > with extensions > generates code for message with field extensions 1`] = ` +exports[`createObjectTypeCode > with extensions > 'generates code for message with field…' 1`] = ` "import { EnumWillRename, PrefixedEnum, PrefixedMessage } from "@testapis/ts-proto/testapis/extensions/extensions"; import { builder } from "../../../../builder"; diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap index 2c5e93ab..ea9b6280 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap +++ b/packages/protoc-gen-pothos/src/dslgen/printers/__snapshots__/oneofUnionType.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`createOneofUnionTypeCode > protobuf-es > generates code for a required oneof union 1`] = ` +exports[`createOneofUnionTypeCode > protobuf-es > 'generates code for a required oneof u…' 1`] = ` "import { builder } from "../../../../builder"; export const OneofParentRequiredOneofMembers$Ref = builder.unionType("OneofParentRequiredOneofMembers", { @@ -22,7 +22,7 @@ export const OneofParentRequiredOneofMembers$Ref = builder.unionType("OneofParen " `; -exports[`createOneofUnionTypeCode > protobuf-es > generates code for a squashed oneof union 1`] = ` +exports[`createOneofUnionTypeCode > protobuf-es > 'generates code for a squashed oneof u…' 1`] = ` "import { builder } from "../../../../builder"; export const TestPrefixPrefixedMessageSquashedMessage$Ref = builder.unionType( @@ -50,7 +50,7 @@ export const TestPrefixPrefixedMessageSquashedMessage$Ref = builder.unionType( " `; -exports[`createOneofUnionTypeCode > protobuf-es > generates code for an optional oneof union 1`] = ` +exports[`createOneofUnionTypeCode > protobuf-es > 'generates code for an optional oneof …' 1`] = ` "import { builder } from "../../../../builder"; export const OneofParentOptionalOneofMembers$Ref = builder.unionType("OneofParentOptionalOneofMembers", { @@ -71,7 +71,7 @@ export const OneofParentOptionalOneofMembers$Ref = builder.unionType("OneofParen " `; -exports[`createOneofUnionTypeCode > ts-proto > generates code for a required oneof union 1`] = ` +exports[`createOneofUnionTypeCode > ts-proto > 'generates code for a required oneof u…' 1`] = ` "import { builder } from "../../../../builder"; export const OneofParentRequiredOneofMembers$Ref = builder.unionType("OneofParentRequiredOneofMembers", { @@ -93,7 +93,7 @@ export const OneofParentRequiredOneofMembers$Ref = builder.unionType("OneofParen " `; -exports[`createOneofUnionTypeCode > ts-proto > generates code for a squashed oneof union 1`] = ` +exports[`createOneofUnionTypeCode > ts-proto > 'generates code for a squashed oneof u…' 1`] = ` "import { builder } from "../../../../builder"; export const TestPrefixPrefixedMessageSquashedMessage$Ref = builder.unionType( @@ -121,7 +121,7 @@ export const TestPrefixPrefixedMessageSquashedMessage$Ref = builder.unionType( " `; -exports[`createOneofUnionTypeCode > ts-proto > generates code for an optional oneof union 1`] = ` +exports[`createOneofUnionTypeCode > ts-proto > 'generates code for an optional oneof …' 1`] = ` "import { builder } from "../../../../builder"; export const OneofParentOptionalOneofMembers$Ref = builder.unionType("OneofParentOptionalOneofMembers", { @@ -142,7 +142,7 @@ export const OneofParentOptionalOneofMembers$Ref = builder.unionType("OneofParen " `; -exports[`createOneofUnionTypeCode > ts-proto > generates code for imported oneof member 1`] = ` +exports[`createOneofUnionTypeCode > ts-proto > 'generates code for imported oneof mem…' 1`] = ` "import { builder } from "../../../../../builder"; import { OneofMember1$Ref, OneofMember2$Ref } from "./member.pothos"; @@ -164,7 +164,7 @@ export const OneofParentOneofField$Ref = builder.unionType("OneofParentOneofFiel " `; -exports[`createOneofUnionTypeCode > with file layout graphql_type > generates code with correct imports for graphql_type layout 1`] = ` +exports[`createOneofUnionTypeCode > with file layout graphql_type > 'generates code with correct imports f…' 1`] = ` "import { builder } from "../../../../builder"; import { OneofMemberMessage1$Ref } from "./OneofMemberMessage1.pothos"; import { OneofMemberMessage2$Ref } from "./OneofMemberMessage2.pothos"; diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts index 9c8dc75e..eeee0e28 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts @@ -52,9 +52,24 @@ function generateEnumTypeCode( return code.toString(); } -describe("createEnumTypeCode", () => { - describe("ts-proto", () => { - const options: PothosPrinterOptions = { +type TestCase = { + test: string; + args: { + packageName: TestapisPackage; + enumTypeNameInProto: string; + }; +}; + +type TestSuite = { + suite: string; + options: PothosPrinterOptions; + cases: TestCase[]; +}; + +const testSuites: TestSuite[] = [ + { + suite: "ts-proto", + options: { dsl: "pothos", protobuf: "ts-proto" as const, importPrefix: "@testapis/ts-proto", @@ -64,43 +79,41 @@ describe("createEnumTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code for a simple enum", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnum", options); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode( - "testapis.enums", - "MyEnumWithoutUnspecified", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for nested enum", () => { - const code = generateEnumTypeCode( - "testapis.nested", - "ParentMessage.NestedEnum", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for enum with extensions", () => { - const code = generateEnumTypeCode( - "testapis.extensions", - "PrefixedEnum", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("protobuf-es", () => { - const options: PothosPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple enum", + args: { + packageName: "testapis.enums", + enumTypeNameInProto: "MyEnum", + }, + }, + { + test: "generates code for an enum without unspecified", + args: { + packageName: "testapis.enums", + enumTypeNameInProto: "MyEnumWithoutUnspecified", + }, + }, + { + test: "generates code for nested enum", + args: { + packageName: "testapis.nested", + enumTypeNameInProto: "ParentMessage.NestedEnum", + }, + }, + { + test: "generates code for enum with extensions", + args: { + packageName: "testapis.extensions", + enumTypeNameInProto: "PrefixedEnum", + }, + }, + ], + }, + { + suite: "protobuf-es", + options: { dsl: "pothos", protobuf: "protobuf-es" as const, importPrefix: "@testapis/protobuf-es", @@ -110,29 +123,44 @@ describe("createEnumTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code for a simple enum", () => { - const code = generateEnumTypeCode("testapis.enums", "MyEnum", options); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an enum without unspecified", () => { - const code = generateEnumTypeCode( - "testapis.enums", - "MyEnumWithoutUnspecified", - options, - ); - expect(code).toMatchSnapshot(); - }); + }, + cases: [ + { + test: "generates code for a simple enum", + args: { + packageName: "testapis.enums", + enumTypeNameInProto: "MyEnum", + }, + }, + { + test: "generates code for an enum without unspecified", + args: { + packageName: "testapis.enums", + enumTypeNameInProto: "MyEnumWithoutUnspecified", + }, + }, + { + test: "generates code for nested enum", + args: { + packageName: "testapis.nested", + enumTypeNameInProto: "ParentMessage.NestedEnum", + }, + }, + ], + }, +]; - test("generates code for nested enum", () => { - const code = generateEnumTypeCode( - "testapis.nested", - "ParentMessage.NestedEnum", - options, - ); - expect(code).toMatchSnapshot(); +describe("createEnumTypeCode", () => { + for (const { suite, options, cases } of testSuites) { + describe(suite, () => { + test.each(cases)("$test", ({ args }) => { + const code = generateEnumTypeCode( + args.packageName, + args.enumTypeNameInProto, + options, + ); + expect(code).toMatchSnapshot(); + }); }); - }); -}); + } +}); \ No newline at end of file diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts index 5526d924..9541fd5a 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts @@ -48,9 +48,25 @@ function generateInputObjectTypeCode( return code.toString(); } -describe("createInputObjectTypeCode", () => { - describe("ts-proto", () => { - const options: PothosPrinterOptions = { +type TestCase = { + test: string; + args: { + packageName: TestapisPackage; + typeNameInProto: string; + partialInputs?: boolean; + }; +}; + +type TestSuite = { + suite: string; + options: PothosPrinterOptions; + cases: TestCase[]; +}; + +const testSuites: TestSuite[] = [ + { + suite: "ts-proto", + options: { dsl: "pothos", protobuf: "ts-proto" as const, importPrefix: "@testapis/ts-proto", @@ -60,56 +76,48 @@ describe("createInputObjectTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Primitives", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Message", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode( - "testapis.oneof", - "OneofParent", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode( - "testapis.empty_types", - "EmptyMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for nested input types", () => { - const code = generateInputObjectTypeCode( - "testapis.nested", - "ParentMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("protobuf-es", () => { - const options: PothosPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple input object", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Primitives", + }, + }, + { + test: "generates code for an input object with nested fields", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Message", + }, + }, + { + test: "generates code for an input object with oneof fields", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + }, + }, + { + test: "generates code for empty input object", + args: { + packageName: "testapis.empty_types", + typeNameInProto: "EmptyMessage", + }, + }, + { + test: "generates code for nested input types", + args: { + packageName: "testapis.nested", + typeNameInProto: "ParentMessage", + }, + }, + ], + }, + { + suite: "protobuf-es", + options: { dsl: "pothos", protobuf: "protobuf-es" as const, importPrefix: "@testapis/protobuf-es", @@ -119,47 +127,41 @@ describe("createInputObjectTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code for a simple input object", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Primitives", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an input object with nested fields", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Message", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an input object with oneof fields", () => { - const code = generateInputObjectTypeCode( - "testapis.oneof", - "OneofParent", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for empty input object", () => { - const code = generateInputObjectTypeCode( - "testapis.empty_types", - "EmptyMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("with partial inputs", () => { - const options: PothosPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple input object", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Primitives", + }, + }, + { + test: "generates code for an input object with nested fields", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Message", + }, + }, + { + test: "generates code for an input object with oneof fields", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + }, + }, + { + test: "generates code for empty input object", + args: { + packageName: "testapis.empty_types", + typeNameInProto: "EmptyMessage", + }, + }, + ], + }, + { + suite: "with partial inputs", + options: { dsl: "pothos", protobuf: "ts-proto" as const, importPrefix: "@testapis/ts-proto", @@ -169,16 +171,32 @@ describe("createInputObjectTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; + }, + cases: [ + { + test: "generates code for partial input types", + args: { + packageName: "testapis.primitives", + typeNameInProto: "Message", + partialInputs: true, + }, + }, + ], + }, +]; - test("generates code for partial input types", () => { - const code = generateInputObjectTypeCode( - "testapis.primitives", - "Message", - options, - true, - ); - expect(code).toMatchSnapshot(); +describe("createInputObjectTypeCode", () => { + for (const { suite, options, cases } of testSuites) { + describe(suite, () => { + test.each(cases)("$test", ({ args }) => { + const code = generateInputObjectTypeCode( + args.packageName, + args.typeNameInProto, + options, + args.partialInputs ?? false, + ); + expect(code).toMatchSnapshot(); + }); }); - }); -}); + } +}); \ No newline at end of file diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts index 04ebb69a..2be3b345 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts @@ -43,9 +43,24 @@ function generateObjectTypeCode( return code.toString(); } -describe("createObjectTypeCode", () => { - describe("ts-proto", () => { - const options: PothosPrinterOptions = { +type TestCase = { + test: string; + args: { + packageName: TestapisPackage; + messageTypeName: string; + }; +}; + +type TestSuite = { + suite: string; + options: PothosPrinterOptions; + cases: TestCase[]; +}; + +const testSuites: TestSuite[] = [ + { + suite: "ts-proto", + options: { dsl: "pothos", protobuf: "ts-proto" as const, importPrefix: "@testapis/ts-proto", @@ -55,56 +70,48 @@ describe("createObjectTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code for a simple message", () => { - const code = generateObjectTypeCode( - "testapis.primitives", - "Primitives", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a message with nested fields", () => { - const code = generateObjectTypeCode( - "testapis.primitives", - "Message", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a message with oneofs", () => { - const code = generateObjectTypeCode( - "testapis.oneof", - "OneofParent", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for empty message", () => { - const code = generateObjectTypeCode( - "testapis.empty_types", - "EmptyMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for nested types", () => { - const code = generateObjectTypeCode( - "testapis.nested", - "ParentMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("protobuf-es", () => { - const options: PothosPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple message", + args: { + packageName: "testapis.primitives", + messageTypeName: "Primitives", + }, + }, + { + test: "generates code for a message with nested fields", + args: { + packageName: "testapis.primitives", + messageTypeName: "Message", + }, + }, + { + test: "generates code for a message with oneofs", + args: { + packageName: "testapis.oneof", + messageTypeName: "OneofParent", + }, + }, + { + test: "generates code for empty message", + args: { + packageName: "testapis.empty_types", + messageTypeName: "EmptyMessage", + }, + }, + { + test: "generates code for nested types", + args: { + packageName: "testapis.nested", + messageTypeName: "ParentMessage", + }, + }, + ], + }, + { + suite: "protobuf-es", + options: { dsl: "pothos", protobuf: "protobuf-es" as const, importPrefix: "@testapis/protobuf-es", @@ -114,38 +121,34 @@ describe("createObjectTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code for a simple message", () => { - const code = generateObjectTypeCode( - "testapis.primitives", - "Primitives", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a message with nested fields", () => { - const code = generateObjectTypeCode( - "testapis.primitives", - "Message", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a message with oneofs", () => { - const code = generateObjectTypeCode( - "testapis.oneof", - "OneofParent", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("with extensions", () => { - const options: PothosPrinterOptions = { + }, + cases: [ + { + test: "generates code for a simple message", + args: { + packageName: "testapis.primitives", + messageTypeName: "Primitives", + }, + }, + { + test: "generates code for a message with nested fields", + args: { + packageName: "testapis.primitives", + messageTypeName: "Message", + }, + }, + { + test: "generates code for a message with oneofs", + args: { + packageName: "testapis.oneof", + messageTypeName: "OneofParent", + }, + }, + ], + }, + { + suite: "with extensions", + options: { dsl: "pothos", protobuf: "ts-proto" as const, importPrefix: "@testapis/ts-proto", @@ -155,15 +158,30 @@ describe("createObjectTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; + }, + cases: [ + { + test: "generates code for message with field extensions", + args: { + packageName: "testapis.extensions", + messageTypeName: "PrefixedMessage", + }, + }, + ], + }, +]; - test("generates code for message with field extensions", () => { - const code = generateObjectTypeCode( - "testapis.extensions", - "PrefixedMessage", - options, - ); - expect(code).toMatchSnapshot(); +describe("createObjectTypeCode", () => { + for (const { suite, options, cases } of testSuites) { + describe(suite, () => { + test.each(cases)("$test", ({ args }) => { + const code = generateObjectTypeCode( + args.packageName, + args.messageTypeName, + options, + ); + expect(code).toMatchSnapshot(); + }); }); - }); -}); + } +}); \ No newline at end of file diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts index 07c9d231..52edc70b 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts @@ -90,9 +90,35 @@ function generateSquashedOneofUnionTypeCode( return code.toString(); } -describe("createOneofUnionTypeCode", () => { - describe("ts-proto", () => { - const options: PothosPrinterOptions = { +type OneofTestCase = { + test: string; + args: { + packageName: TestapisPackage; + typeNameInProto: string; + oneofFieldName: string; + }; +}; + +type SquashedTestCase = { + test: string; + args: { + packageName: TestapisPackage; + typeNameInProto: string; + }; +}; + +type TestCase = OneofTestCase | SquashedTestCase; + +type TestSuite = { + suite: string; + options: PothosPrinterOptions; + cases: TestCase[]; +}; + +const testSuites: TestSuite[] = [ + { + suite: "ts-proto", + options: { dsl: "pothos", protobuf: "ts-proto" as const, importPrefix: "@testapis/ts-proto", @@ -102,50 +128,44 @@ describe("createOneofUnionTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code for a required oneof union", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "required_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an optional oneof union", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "optional_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a squashed oneof union", () => { - const code = generateSquashedOneofUnionTypeCode( - "testapis.extensions", - "PrefixedMessage.SquashedMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for imported oneof member", () => { - const code = generateOneofUnionTypeCode( - "testapis.edgecases.import_oneof_member_from_other_file", - "OneofParent", - "oneof_field", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("protobuf-es", () => { - const options: PothosPrinterOptions = { + }, + cases: [ + { + test: "generates code for a required oneof union", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "required_oneof_members", + }, + } as OneofTestCase, + { + test: "generates code for an optional oneof union", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "optional_oneof_members", + }, + } as OneofTestCase, + { + test: "generates code for a squashed oneof union", + args: { + packageName: "testapis.extensions", + typeNameInProto: "PrefixedMessage.SquashedMessage", + }, + } as SquashedTestCase, + { + test: "generates code for imported oneof member", + args: { + packageName: "testapis.edgecases.import_oneof_member_from_other_file", + typeNameInProto: "OneofParent", + oneofFieldName: "oneof_field", + }, + } as OneofTestCase, + ], + }, + { + suite: "protobuf-es", + options: { dsl: "pothos", protobuf: "protobuf-es" as const, importPrefix: "@testapis/protobuf-es", @@ -155,40 +175,36 @@ describe("createOneofUnionTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code for a required oneof union", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "required_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for an optional oneof union", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "optional_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); - }); - - test("generates code for a squashed oneof union", () => { - const code = generateSquashedOneofUnionTypeCode( - "testapis.extensions", - "PrefixedMessage.SquashedMessage", - options, - ); - expect(code).toMatchSnapshot(); - }); - }); - - describe("with file layout graphql_type", () => { - const options: PothosPrinterOptions = { + }, + cases: [ + { + test: "generates code for a required oneof union", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "required_oneof_members", + }, + } as OneofTestCase, + { + test: "generates code for an optional oneof union", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "optional_oneof_members", + }, + } as OneofTestCase, + { + test: "generates code for a squashed oneof union", + args: { + packageName: "testapis.extensions", + typeNameInProto: "PrefixedMessage.SquashedMessage", + }, + } as SquashedTestCase, + ], + }, + { + suite: "with file layout graphql_type", + options: { dsl: "pothos", protobuf: "ts-proto" as const, importPrefix: "@testapis/ts-proto", @@ -198,16 +214,41 @@ describe("createOneofUnionTypeCode", () => { pothos: { builderPath: "../../builder", }, - }; - - test("generates code with correct imports for graphql_type layout", () => { - const code = generateOneofUnionTypeCode( - "testapis.oneof", - "OneofParent", - "required_oneof_members", - options, - ); - expect(code).toMatchSnapshot(); + }, + cases: [ + { + test: "generates code with correct imports for graphql_type layout", + args: { + packageName: "testapis.oneof", + typeNameInProto: "OneofParent", + oneofFieldName: "required_oneof_members", + }, + } as OneofTestCase, + ], + }, +]; + +describe("createOneofUnionTypeCode", () => { + for (const { suite, options, cases } of testSuites) { + describe(suite, () => { + test.each(cases)("$test", ({ args }) => { + if ("oneofFieldName" in args) { + const code = generateOneofUnionTypeCode( + args.packageName, + args.typeNameInProto, + args.oneofFieldName, + options, + ); + expect(code).toMatchSnapshot(); + } else { + const code = generateSquashedOneofUnionTypeCode( + args.packageName, + args.typeNameInProto, + options, + ); + expect(code).toMatchSnapshot(); + } + }); }); - }); -}); + } +}); \ No newline at end of file From 1c0bf9a30831b20cb7d68f0c19cff0c0fc1554c5 Mon Sep 17 00:00:00 2001 From: izumin5210 Date: Sun, 8 Jun 2025 13:33:20 +0900 Subject: [PATCH 27/27] lint --- .../src/dslgen/printers/enumType.test.ts | 2 +- .../src/dslgen/printers/objectType.test.ts | 2 +- .../src/dslgen/printers/oneofUnionType.test.ts | 10 +++++----- .../src/dslgen/printers/enumType.test.ts | 2 +- .../src/dslgen/printers/inputObjectType.test.ts | 2 +- .../src/dslgen/printers/objectType.test.ts | 2 +- .../src/dslgen/printers/oneofUnionType.test.ts | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts index cdb66f1a..c0b1bd48 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/enumType.test.ts @@ -133,4 +133,4 @@ describe("createEnumTypeCode", () => { }); }); } -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts index c269bf04..3c8bfb45 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/objectType.test.ts @@ -150,4 +150,4 @@ describe("createObjectTypeCode", () => { }); }); } -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts index af828f75..dc43c16f 100644 --- a/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-nexus/src/dslgen/printers/oneofUnionType.test.ts @@ -1,9 +1,9 @@ import { createFileRegistry } from "@bufbuild/protobuf"; -import { +import { OneofUnionType, SquashedOneofUnionType, type TypeOptions, - defaultScalarMapping + defaultScalarMapping, } from "@proto-graphql/codegen-core"; import { type TestapisPackage, @@ -26,7 +26,7 @@ function generateOneofUnionTypeCode( }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet) + const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { throw new Error( @@ -64,7 +64,7 @@ function generateSquashedOneofUnionTypeCode( }; const descSet = getTestapisFileDescriptorSet(packageName); - const registry = createFileRegistry(descSet) + const registry = createFileRegistry(descSet); const descMsg = registry.getMessage(`${packageName}.${typeNameInProto}`); if (descMsg === undefined) { throw new Error( @@ -206,4 +206,4 @@ describe("createOneofUnionTypeCode", () => { }); }); } -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts index eeee0e28..2aba6df2 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/enumType.test.ts @@ -163,4 +163,4 @@ describe("createEnumTypeCode", () => { }); }); } -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts index 9541fd5a..cff721e4 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/inputObjectType.test.ts @@ -199,4 +199,4 @@ describe("createInputObjectTypeCode", () => { }); }); } -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts index 2be3b345..bd6ce02a 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/objectType.test.ts @@ -184,4 +184,4 @@ describe("createObjectTypeCode", () => { }); }); } -}); \ No newline at end of file +}); diff --git a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts index 52edc70b..18d43764 100644 --- a/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts +++ b/packages/protoc-gen-pothos/src/dslgen/printers/oneofUnionType.test.ts @@ -251,4 +251,4 @@ describe("createOneofUnionTypeCode", () => { }); }); } -}); \ No newline at end of file +});