Skip to content

Switch serializer tests to test round trips + remove deprecated grpc.load() #1699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from

Conversation

rsgowman
Copy link
Member

  • Round trip testing move this port closer to the others.
  • Also changes the way protos in the test are constructed

Separately (though related) this PR also removes the deprecated grpc.load() method. The old tests relied on grpc.load() exposing the proto constructors (which the new protoLoader does not do.)

@rsgowman rsgowman self-assigned this Apr 15, 2019
);
const proto: api.Value = {
timestampValue: (expectedJson[i] as unknown) as string
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why the proto's timestampValue is typed as a string? But I also have no idea how protobufjs works.

I also needed the same trick down below in the bytes section, though at least in that case, it sort of makes sense... since at least some of the time this is actually a base64 encoded string.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the backend accepts either a timestamp proto or a UTC string and our proto .d.ts file is currently typed as a string (https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/protos/firestore_proto_api.d.ts#L356). Per the conversation in chat, I think we can probably go ahead and just fix the d.ts file (so it accepts either format I guess).

// clang-format on
});
});
const proto: api.Value = expectedJson;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding this comment which I deleted: "Compare the raw object ... in the event of an inequality".

After my change (I didn't test it before my change), I found this to be incorrect. If I introduced an inequality and ran the tests a few times, the tests always completed with the error string properly generated. As a result, I eliminated the special casing here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use jasmine anymore, so this is very likely fine. :) Thanks!

Copy link
Contributor

@mikelehen mikelehen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't done a fully detailed review yet but at a high-level I think I'm mostly on board with this (thanks a ton for tackling all of this). I left a few comments and I do have a bit of reservation about no longer constructing actual proto objects. As-is if you make a mistake in serializer.ts with the JSON we generate such that the JSON is wrong, the serializer tests won't notice as long as you make the mistake in both the serialization and deserialization methods. In theory the JSON representation of the protos is strongly typed based on https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/protos/firestore_proto_api.d.ts#L1, but there are some nuances / hacks because grpc and webchannel speak slightly different dialects of JSON protos so it's still conceivable we could screw something up... though presumably integration tests would catch it.

If we wanted to round-trip through the actual proto object that gRPC uses (similar to the way the tests did before), I think we could just load the protos directly in our tests via protobufjs instead of going through @grpc/proto-loader and then we'd get access to the constructors as before. Basically just copy the gprc/proto-loader loadSync() code: https://github.com/grpc/grpc-node/blob/073dc563858a2d74bd114e5cc50d02203123078f/packages/proto-loader/src/index.ts#L314

I'd be a fan of that change, but if you don't feel like taking it on, I can probably get behind this PR as-is or else I might take a stab at it myself...


// Beware that converting fields to camel case does not convert the tag fields
// in oneof groups (!!!). This will likely be fixed when we upgrade to
// protobufjs 6.x
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment is probably stale since:

  1. gprc/proto-loader pulls in protobufjs 6.8.6.
  2. This comment used to be referring to our usage of 'convertFieldsToCamelCase' which no longer exists.

I think this comment is referring to the fact that the tag fields are named value_type, response_type, etc. instead of valueType, responseType, etc. And I guess this is not going to change (since you didn't have to change that code in serializer.ts), so I would just drop the comment.

defaults: true,
oneofs: true,
includeDirs: [root]
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's worth commenting these options a bit so somebody (future me) doesn't have to go look up the grpc docs to figure out what they mean...

@@ -0,0 +1,882 @@
// Protocol Buffers - Google's data interchange format
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// as optional fields. To stay consistent, we'll still test for the given
// tag's presence here, though we won't test for absence of all other
// fields.
expect(actual[tag]).to.not.be.undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm...

  1. Since the following expectation fully verifies the contents of actual, it seems like this must be redundant, no? Or is this meant to verify the test is written correctly? If so, consider using an assert() instead.
  2. The old code verified that the serializer generated protos that contain value_type, which I think was important. If I'm understanding correctly from
    * Checks for a specific oneof tag in a protocol buffer message.
    , gRPC would produce output messages without value_type but require input messages to have value_type. But I wonder if that asymmetry still exists (or if we can avoid it by tweaking the options we pass to loadSync()).

So I think we need to rework the test to verify the serializer generates (and accepts) value_type or else rework the serializer so we don't need the hasTag() mess (which would be amazing if it works).

);
const proto: api.Value = {
timestampValue: (expectedJson[i] as unknown) as string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the backend accepts either a timestamp proto or a UTC string and our proto .d.ts file is currently typed as a string (https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/protos/firestore_proto_api.d.ts#L356). Per the conversation in chat, I think we can probably go ahead and just fix the d.ts file (so it accepts either format I guess).

// clang-format on
});
});
const proto: api.Value = expectedJson;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use jasmine anymore, so this is very likely fine. :) Thanks!

@@ -31,6 +31,7 @@
"@firebase/logger": "0.1.11",
"@firebase/webchannel-wrapper": "0.2.17",
"grpc": "1.19.0",
"@grpc/proto-loader": "^0.4.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to run yarn or yarn install from the root and check in the resulting yarn.lock file.

new fieldValue.BlobValue(blob)
);
expectRoundTrip(objValue, proto, 'mapValue');
// clang-format on
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this?

@mikelehen mikelehen assigned rsgowman and unassigned mikelehen Apr 16, 2019
@rsgowman
Copy link
Member Author

I haven't done a fully detailed review yet but at a high-level I think I'm mostly on board with this (thanks a ton for tackling all of this). I left a few comments and I do have a bit of reservation about no longer constructing actual proto objects. As-is if you make a mistake in serializer.ts with the JSON we generate such that the JSON is wrong, the serializer tests won't notice as long as you make the mistake in both the serialization and deserialization methods. In theory the JSON representation of the protos is strongly typed based on https://github.com/firebase/firebase-js-sdk/blob/master/packages/firestore/src/protos/firestore_proto_api.d.ts#L1, but there are some nuances / hacks because grpc and webchannel speak slightly different dialects of JSON protos so it's still conceivable we could screw something up... though presumably integration tests would catch it.

If we wanted to round-trip through the actual proto object that gRPC uses (similar to the way the tests did before), I think we could just load the protos directly in our tests via protobufjs instead of going through @grpc/proto-loader and then we'd get access to the constructors as before. Basically just copy the gprc/proto-loader loadSync() code: https://github.com/grpc/grpc-node/blob/073dc563858a2d74bd114e5cc50d02203123078f/packages/proto-loader/src/index.ts#L314

I'd be a fan of that change, but if you don't feel like taking it on, I can probably get behind this PR as-is or else I might take a stab at it myself...

Take a look at this: #1669. It's my first attempt; I don't like it as much, but I think it does what you're suggesting. I think it may be possible to meld these two together, so that we still get access to the underlying ctors, but also can eliminate all the fromFoo tests. If that's what you had in mind, then I'll tackle it.

@mikelehen
Copy link
Contributor

Oh! Yes, #1669 is very much what I had in mind, but yeah, I think combining that with all the cleanup you did here would be great. Basically make your "roundtrip" methods also construct the real proto instead of just the JSON representation (maybe even encode to bytes and back to JSON?). That would make these tests really nice.

@wilhuff
Copy link
Contributor

wilhuff commented Apr 16, 2019

Could we meld these two approaches together by essentially running the round trip twice? Once directly and once through protos? This would have the same benefit of reducing the volume of tests, while giving us the same net coverage.

Note that this is at least conceptually similar to what we're doing in the C++ serialization tests, where we're validating that our serialization generates bytes that libprotobuf can parse. There we modified the round trip validation to check both paths. Though the particulars are different here, could the same approach work?

@wilhuff
Copy link
Contributor

wilhuff commented Apr 16, 2019

LOL. I wish I refreshed before posting.

@rsgowman
Copy link
Member Author

rsgowman commented May 2, 2019

Closing in favour of #1669 (which contains the relevant bits from this pr)

@rsgowman rsgowman closed this May 2, 2019
@firebase firebase locked and limited conversation to collaborators Oct 12, 2019
@wilhuff wilhuff deleted the rsgowman/grpc_load_2 branch March 13, 2020 16:26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants