mirror of
https://github.com/hanzoai/protobuf.js.git
synced 2026-09-24 17:02:08 +00:00
280 lines
9.4 KiB
JavaScript
280 lines
9.4 KiB
JavaScript
var tape = require("tape");
|
|
|
|
var protobuf = require("..");
|
|
|
|
tape.test("reflected enums", function(test) {
|
|
|
|
var enm = new protobuf.Enum("Test", {
|
|
a: 1,
|
|
b: 2,
|
|
});
|
|
|
|
var enm_allow_alias = new protobuf.Enum( 'AliasTest',
|
|
{ a: 0 }, { allow_alias: true } );
|
|
|
|
test.throws(function() {
|
|
new protobuf.Enum("Test", true);
|
|
}, TypeError, "should throw if values is specified but not an object");
|
|
|
|
test.same(enm.valuesById, {
|
|
1: "a",
|
|
2: "b"
|
|
}, "should also expose their values by id");
|
|
|
|
test.throws(function() {
|
|
enm.add(2, 2);
|
|
}, TypeError, "should throw if name is not a string");
|
|
|
|
test.throws(function() {
|
|
enm.add("c", 1.5);
|
|
}, TypeError, "should throw if id is not an integer");
|
|
|
|
test.throws(function() {
|
|
enm.add("b", 2);
|
|
}, Error, "should throw if name is a duplicate");
|
|
|
|
test.throws(function() {
|
|
enm.add("c", 2);
|
|
}, Error, "should throw if id is a duplicate, without allow_alias option");
|
|
|
|
enm.add("c", 3);
|
|
test.same(enm.values, {
|
|
a: 1,
|
|
b: 2,
|
|
c: 3
|
|
}, "should allow adding new values");
|
|
test.same(enm.valuesById, {
|
|
1: "a",
|
|
2: "b",
|
|
3: "c"
|
|
}, "should also expose any new values by id");
|
|
|
|
var special = new protobuf.Enum("Special", {});
|
|
special.add("__proto__", 1);
|
|
test.notOk(Object.prototype.hasOwnProperty.call(special.values, "__proto__"), "should ignore reserved enum value names");
|
|
test.equal(special.values.constructor, undefined, "should not expose inherited object properties as enum values");
|
|
|
|
special.add("constructor", 2);
|
|
test.equal(special.values.constructor, 2, "should allow enum value names that shadow object properties");
|
|
test.equal(special.valuesById[2], "constructor", "should expose shadowing enum value names by id");
|
|
|
|
test.throws(function() {
|
|
enm.remove(1);
|
|
}, TypeError, "should throw if name is not a string");
|
|
|
|
test.throws(function() {
|
|
enm.remove("d");
|
|
}, Error, "should throw if name is not present");
|
|
|
|
enm.remove("b");
|
|
test.same(enm.values, {
|
|
a: 1,
|
|
c: 3
|
|
}, "should allow removing existing values");
|
|
test.same(enm.valuesById, {
|
|
1: "a",
|
|
3: "c"
|
|
}, "should no longer expose any removed values by id");
|
|
|
|
test.same(enm.toJSON(), {
|
|
values: {
|
|
a: 1,
|
|
c: 3
|
|
}
|
|
}, "should export values to JSON");
|
|
|
|
enm_allow_alias.add( 'b', 0 );
|
|
test.same( enm_allow_alias.values, {
|
|
a: 0,
|
|
b: 0
|
|
});
|
|
|
|
enm.add('e', 4, undefined, {'(test_option)': 'test_value'});
|
|
test.same( enm.valuesOptions, {
|
|
'e': {
|
|
'(test_option)': 'test_value'
|
|
}
|
|
});
|
|
enm.remove("e");
|
|
test.same( enm.valuesOptions, {}, "should clean up value options");
|
|
|
|
var enmFromJSON = protobuf.Enum.fromJSON("FromJSON", {
|
|
values: { a: 0 },
|
|
valuesOptions: { a: { deprecated: true } }
|
|
});
|
|
test.same(enmFromJSON.valuesOptions, { a: { deprecated: true } }, "should import value options from JSON");
|
|
|
|
var shadowFromJSON = protobuf.Enum.fromJSON("ShadowFromJSON", {
|
|
values: { toString: 0 }
|
|
});
|
|
test.equal(shadowFromJSON.values.toString, 0, "should import shadowing enum value names from JSON");
|
|
|
|
enm.reserved = [[100,200], "BAD_NAME"];
|
|
test.throws(function() {
|
|
enm.add("d", 101);
|
|
}, Error, "should throw if id is a reserved number");
|
|
|
|
test.throws(function() {
|
|
enm.add("d", 200);
|
|
}, Error, "should throw if id is a reserved range end");
|
|
|
|
enm.reserved = [[100,100], "BAD_NAME"];
|
|
test.throws(function() {
|
|
enm.add("d", 100);
|
|
}, Error, "should throw if id is a reserved singleton number");
|
|
|
|
test.throws(function() {
|
|
enm.add("BAD_NAME", 5);
|
|
}, Error, "should throw if id is a reserved name");
|
|
|
|
test.end();
|
|
});
|
|
|
|
tape.test("feature resolution legacy proto3", function(test) {
|
|
var json = {
|
|
values: {
|
|
a: 0, b: 1
|
|
}
|
|
};
|
|
var messageJson = {
|
|
fields: {},
|
|
nested: { Enum: { values: {
|
|
a: 0, b:1
|
|
} } }
|
|
};
|
|
var root = new protobuf.Root();
|
|
var Enum = protobuf.Enum.fromJSON("Enum", json);
|
|
var Message = protobuf.Type.fromJSON("Message", messageJson)
|
|
var Nested = Message.nested.Enum;
|
|
root.add(Enum).add(Message).resolveAll();
|
|
|
|
test.same(Enum.toJSON(), json, "JSON should roundtrip");
|
|
test.same(Message.toJSON(), messageJson, "container JSON should roundtrip");
|
|
test.same(Nested.toJSON(), messageJson.nested.Enum, "nested JSON should roundtrip");
|
|
|
|
test.equal(Enum._edition, "proto3", "should infer proto3 syntax");
|
|
test.equal(Enum._features.enum_type, "OPEN", "should be open by default");
|
|
|
|
test.equal(Nested._edition, null, "should not set edition");
|
|
test.equal(Nested._features.enum_type, "OPEN", "should be open by default");
|
|
|
|
test.end();
|
|
});
|
|
|
|
tape.test("feature resolution skips unsafe feature keys", function(test) {
|
|
var enumFeatures = { enum_type: "CLOSED" };
|
|
Object.defineProperty(enumFeatures, "__proto__", {
|
|
value: { polluted: true },
|
|
enumerable: true
|
|
});
|
|
enumFeatures.prototype = { polluted: true };
|
|
enumFeatures.constructor = { polluted: true };
|
|
|
|
var valueFeatures = { field_presence: "EXPLICIT" };
|
|
Object.defineProperty(valueFeatures, "__proto__", {
|
|
value: { polluted: true },
|
|
enumerable: true
|
|
});
|
|
valueFeatures.prototype = { polluted: true };
|
|
valueFeatures.constructor = { polluted: true };
|
|
|
|
var root = protobuf.Root.fromJSON({
|
|
nested: {
|
|
Enum: {
|
|
edition: "2023",
|
|
options: { features: enumFeatures },
|
|
values: {
|
|
a: 0
|
|
},
|
|
valuesOptions: {
|
|
a: {
|
|
features: valueFeatures
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}).resolveAll();
|
|
var Enum = root.lookupEnum("Enum");
|
|
|
|
test.equal(Enum._features.enum_type, "CLOSED", "should keep regular enum features");
|
|
test.equal(Object.getPrototypeOf(Enum._features).polluted, undefined, "should not alter enum feature prototype");
|
|
test.notOk(Object.prototype.hasOwnProperty.call(Enum._features, "__proto__"), "should skip unsafe enum feature key __proto__");
|
|
test.notOk(Object.prototype.hasOwnProperty.call(Enum._features, "prototype"), "should skip unsafe enum feature key prototype");
|
|
test.notOk(Object.prototype.hasOwnProperty.call(Enum._features, "constructor"), "should skip unsafe enum feature key constructor");
|
|
|
|
test.equal(Enum._valuesFeatures.a.field_presence, "EXPLICIT", "should keep regular enum value features");
|
|
test.equal(Object.getPrototypeOf(Enum._valuesFeatures.a).polluted, undefined, "should not alter enum value feature prototype");
|
|
test.notOk(Object.prototype.hasOwnProperty.call(Enum._valuesFeatures.a, "__proto__"), "should skip unsafe enum value feature key __proto__");
|
|
test.notOk(Object.prototype.hasOwnProperty.call(Enum._valuesFeatures.a, "prototype"), "should skip unsafe enum value feature key prototype");
|
|
test.notOk(Object.prototype.hasOwnProperty.call(Enum._valuesFeatures.a, "constructor"), "should skip unsafe enum value feature key constructor");
|
|
|
|
test.end();
|
|
});
|
|
|
|
tape.test("feature resolution proto2", function(test) {
|
|
var json = {
|
|
edition: "proto2",
|
|
values: {
|
|
a: 0, b: 1
|
|
}
|
|
};
|
|
var messageJson = {
|
|
edition: "proto2",
|
|
fields: {},
|
|
nested: { Enum: { values: {
|
|
a: 0, b: 1
|
|
} } }
|
|
};
|
|
var root = new protobuf.Root();
|
|
var Enum = protobuf.Enum.fromJSON("Enum", json);
|
|
var Message = protobuf.Type.fromJSON("Message", messageJson)
|
|
var Nested = Message.nested.Enum;
|
|
root.add(Enum).add(Message).resolveAll();
|
|
|
|
test.same(Enum.toJSON(), json, "JSON should roundtrip");
|
|
test.same(Message.toJSON(), messageJson, "container JSON should roundtrip");
|
|
test.same(Nested.toJSON(), messageJson.nested.Enum, "nested JSON should roundtrip");
|
|
|
|
test.equal(Enum._edition, "proto2", "should set edition");
|
|
test.equal(Enum._features.enum_type, "CLOSED", "should be closed by default");
|
|
|
|
test.equal(Nested._edition, null, "should not set edition");
|
|
test.equal(Nested._features.enum_type, "CLOSED", "should be closed by default");
|
|
|
|
test.end();
|
|
});
|
|
|
|
tape.test("feature resolution legacy proto3", function(test) {
|
|
var json = {
|
|
edition: "2023",
|
|
values: {
|
|
a: 0, b: 1
|
|
}
|
|
};
|
|
var messageJson = {
|
|
edition: "2023",
|
|
options: { features: { enum_type: "CLOSED" } },
|
|
fields: {},
|
|
nested: { Enum: { values: {
|
|
a: 0, b: 1
|
|
} } }
|
|
};
|
|
var root = new protobuf.Root();
|
|
var Enum = protobuf.Enum.fromJSON("Enum", json);
|
|
var Message = protobuf.Type.fromJSON("Message", messageJson)
|
|
var Nested = Message.nested.Enum;
|
|
root.add(Enum).add(Message).resolveAll();
|
|
|
|
test.same(Enum.toJSON(), json, "JSON should roundtrip");
|
|
test.same(Message.toJSON(), messageJson, "container JSON should roundtrip");
|
|
test.same(Nested.toJSON(), messageJson.nested.Enum, "nested JSON should roundtrip");
|
|
|
|
test.equal(Enum._edition, "2023", "should set edition");
|
|
test.equal(Enum._features.enum_type, "OPEN", "should be open by default");
|
|
|
|
test.equal(Nested._edition, null, "should not set edition");
|
|
test.equal(Nested._features.enum_type, "CLOSED", "should inherit override");
|
|
|
|
test.end();
|
|
});
|