234 attempt to fix js error Uncaught SyntaxError: Unexpected identifier 'hidden'

This commit is contained in:
2026-04-27 17:57:24 +03:00
parent 8a32a0d5f0
commit 74c073bd7e
149 changed files with 14361 additions and 8886 deletions
@@ -0,0 +1,80 @@
component extends="testbox.system.BaseSpec" {
function run() {
describe("BaseDeserializer", function() {
beforeEach(function() {
variables.deserializer = new core.baseDeserializer();
});
describe("getFromForm()", function() {
it("should parse simple form data", function() {
var body = "name=John&age=30";
var result = deserializer.getFromForm(body);
expect(result.name).toBe("John");
expect(result.age).toBe("30");
});
it("should parse URL-encoded values", function() {
var body = "name=John%20Doe&city=New%20York";
var result = deserializer.getFromForm(body);
expect(result.name).toBe("John Doe");
expect(result.city).toBe("New York");
});
it("should handle empty values", function() {
var body = "name=John&nickname=";
var result = deserializer.getFromForm(body);
expect(result.name).toBe("John");
expect(result.nickname).toBe("");
});
it("should handle multiple values for same key as list", function() {
var body = "color=red&color=blue&color=green";
var result = deserializer.getFromForm(body);
expect(result.color).toInclude("red");
expect(result.color).toInclude("blue");
expect(result.color).toInclude("green");
});
it("should handle special characters when URL encoded", function() {
var body = "query=" & urlEncodedFormat("foo=bar&baz=qux");
var result = deserializer.getFromForm(body);
expect(result.query).toBe("foo=bar&baz=qux");
});
it("should handle plus signs as spaces", function() {
// Note: urlDecode handles + as space
var body = "name=John+Doe";
var result = deserializer.getFromForm(body);
expect(result.name).toBe("John Doe");
});
});
describe("taffy:mime metadata", function() {
it("should have taffy:mime for application/x-www-form-urlencoded", function() {
var metadata = getMetadata(deserializer);
var func = "";
for (var f in metadata.functions) {
if (f.name == "getFromForm") {
func = f;
break;
}
}
expect(func).notToBeEmpty();
var hasMime = structKeyExists(func, "taffy:mime")
|| structKeyExists(func, "taffy_mime");
expect(hasMime).toBeTrue();
});
});
});
}
}
@@ -0,0 +1,325 @@
component extends="testbox.system.BaseSpec" {
function run() {
describe("BaseSerializer", function() {
beforeEach(function() {
variables.serializer = new core.baseSerializer();
});
describe("setData() and getData()", function() {
it("should store and retrieve simple string data", function() {
var data = "Hello World";
serializer.setData(data);
expect(serializer.getData()).toBe(data);
});
it("should store and retrieve struct data", function() {
var data = { name: "Test", value: 123 };
serializer.setData(data);
var result = serializer.getData();
expect(result.name).toBe("Test");
expect(result.value).toBe(123);
});
it("should store and retrieve array data", function() {
var data = [1, 2, 3, 4, 5];
serializer.setData(data);
expect(serializer.getData()).toBeArray();
expect(arrayLen(serializer.getData())).toBe(5);
});
it("should return this for method chaining", function() {
var result = serializer.setData("test");
expect(new tests.helpers.MetadataHelper().extendsClass(result, "baseSerializer")).toBeTrue();
});
it("should set type to textual (1) when setting data", function() {
serializer.setData("test");
expect(serializer.getType()).toBe("textual");
});
});
describe("withStatus()", function() {
it("should set status code with default text", function() {
serializer.withStatus(200);
expect(serializer.getStatus()).toBe(200);
expect(serializer.getStatusText()).toBe("OK");
});
it("should set status code 201 with Created text", function() {
serializer.withStatus(201);
expect(serializer.getStatus()).toBe(201);
expect(serializer.getStatusText()).toBe("Created");
});
it("should set status code 404 with Not Found text", function() {
serializer.withStatus(404);
expect(serializer.getStatus()).toBe(404);
expect(serializer.getStatusText()).toBe("Not Found");
});
it("should set status code 500 with Internal Server Error text", function() {
serializer.withStatus(500);
expect(serializer.getStatus()).toBe(500);
expect(serializer.getStatusText()).toBe("Internal Server Error");
});
it("should allow custom status text", function() {
serializer.withStatus(418, "I'm a teapot - Custom");
expect(serializer.getStatus()).toBe(418);
expect(serializer.getStatusText()).toBe("I'm a teapot - Custom");
});
it("should use lookup table for known status codes", function() {
serializer.withStatus(418);
expect(serializer.getStatusText()).toBe("I'm a teapot");
});
it("should return this for method chaining", function() {
var result = serializer.withStatus(200);
expect(new tests.helpers.MetadataHelper().extendsClass(result, "baseSerializer")).toBeTrue();
});
it("should handle all 2xx status codes", function() {
serializer.withStatus(204);
expect(serializer.getStatusText()).toBe("No Content");
serializer.withStatus(206);
expect(serializer.getStatusText()).toBe("Partial Content");
});
it("should handle 3xx redirect status codes", function() {
serializer.withStatus(301);
expect(serializer.getStatusText()).toBe("Moved Permanently");
serializer.withStatus(302);
expect(serializer.getStatusText()).toBe("Found");
serializer.withStatus(304);
expect(serializer.getStatusText()).toBe("Not Modified");
});
it("should handle 4xx client error status codes", function() {
serializer.withStatus(400);
expect(serializer.getStatusText()).toBe("Bad Request");
serializer.withStatus(401);
expect(serializer.getStatusText()).toBe("Unauthorized");
serializer.withStatus(403);
expect(serializer.getStatusText()).toBe("Forbidden");
serializer.withStatus(405);
expect(serializer.getStatusText()).toBe("Method Not Allowed");
serializer.withStatus(422);
expect(serializer.getStatusText()).toBe("Unprocessable Entity");
serializer.withStatus(429);
expect(serializer.getStatusText()).toBe("Too Many Requests");
});
it("should handle 5xx server error status codes", function() {
serializer.withStatus(501);
expect(serializer.getStatusText()).toBe("Not Implemented");
serializer.withStatus(502);
expect(serializer.getStatusText()).toBe("Bad Gateway");
serializer.withStatus(503);
expect(serializer.getStatusText()).toBe("Service Unavailable");
serializer.withStatus(504);
expect(serializer.getStatusText()).toBe("Gateway Timeout");
});
});
describe("withHeaders()", function() {
it("should set custom headers", function() {
var headers = { "X-Custom-Header": "value1", "X-Another": "value2" };
serializer.withHeaders(headers);
var result = serializer.getHeaders();
expect(result["X-Custom-Header"]).toBe("value1");
expect(result["X-Another"]).toBe("value2");
});
it("should return this for method chaining", function() {
var result = serializer.withHeaders({});
expect(new tests.helpers.MetadataHelper().extendsClass(result, "baseSerializer")).toBeTrue();
});
it("should return empty struct when no headers set", function() {
var result = serializer.getHeaders();
expect(result).toBeStruct();
expect(structIsEmpty(result)).toBeTrue();
});
});
describe("noData()", function() {
it("should return serializer instance when noDataSends204NoContent is false", function() {
serializer.setNoDataSends204NoContent(false);
var result = serializer.noData();
expect(new tests.helpers.MetadataHelper().extendsClass(result, "baseSerializer")).toBeTrue();
expect(result.getStatus()).toBe(200);
});
it("should return 204 status when noDataSends204NoContent is true", function() {
serializer.setNoDataSends204NoContent(true);
var result = serializer.noData();
expect(result.getStatus()).toBe(204);
});
it("should default to false (no 204) without explicit configuration", function() {
var result = serializer.noData();
expect(result.getStatus()).toBe(200);
});
});
describe("noContent()", function() {
it("should set status to 204 No Content", function() {
var result = serializer.noContent();
expect(result.getStatus()).toBe(204);
expect(result.getStatusText()).toBe("No Content");
});
it("should set Content-Type header to text/plain", function() {
var result = serializer.noContent();
var headers = result.getHeaders();
expect(headers["Content-Type"]).toBe("text/plain");
});
});
describe("File streaming methods", function() {
describe("setFileName()", function() {
it("should store filename for streaming", function() {
serializer.setFileName("/path/to/file.pdf");
expect(serializer.getFileName()).toBe("/path/to/file.pdf");
});
it("should set type to filename (2)", function() {
serializer.setFileName("/path/to/file.pdf");
expect(serializer.getType()).toBe("filename");
});
it("should return this for method chaining", function() {
var result = serializer.setFileName("/path/to/file.pdf");
expect(new tests.helpers.MetadataHelper().extendsClass(result, "baseSerializer")).toBeTrue();
});
});
describe("setFileData()", function() {
it("should store binary data for streaming", function() {
var binaryData = toBinary(toBase64("test data"));
serializer.setFileData(binaryData);
expect(serializer.getFileData()).toBe(binaryData);
});
it("should set type to filedata (3)", function() {
serializer.setFileData(toBinary(toBase64("test")));
expect(serializer.getType()).toBe("filedata");
});
});
describe("setImageData()", function() {
it("should store image data for streaming", function() {
var imageData = toBinary(toBase64("fake image data"));
serializer.setImageData(imageData);
expect(serializer.getImageData()).notToBeNull();
});
it("should set type to imagedata (4)", function() {
serializer.setImageData(toBinary(toBase64("test")));
expect(serializer.getType()).toBe("imagedata");
});
it("should convert non-binary data to binary", function() {
serializer.setImageData("test string");
expect(isBinary(serializer.getImageData())).toBeTrue();
});
});
describe("withMime()", function() {
it("should set mime type for file streaming", function() {
serializer.withMime("application/pdf");
expect(serializer.getFileMime()).toBe("application/pdf");
});
it("should return this for method chaining", function() {
var result = serializer.withMime("image/jpeg");
expect(new tests.helpers.MetadataHelper().extendsClass(result, "baseSerializer")).toBeTrue();
});
});
describe("andDelete()", function() {
it("should set delete flag to true", function() {
serializer.andDelete(true);
expect(serializer.getDeleteFile()).toBeTrue();
});
it("should set delete flag to false", function() {
serializer.andDelete(false);
expect(serializer.getDeleteFile()).toBeFalse();
});
it("should return this for method chaining", function() {
var result = serializer.andDelete(true);
expect(new tests.helpers.MetadataHelper().extendsClass(result, "baseSerializer")).toBeTrue();
});
});
});
describe("Method chaining", function() {
it("should support fluent interface for multiple operations", function() {
var result = serializer
.setData({ message: "test" })
.withStatus(201, "Created")
.withHeaders({ "X-Custom": "value" });
expect(result.getData().message).toBe("test");
expect(result.getStatus()).toBe(201);
expect(result.getHeaders()["X-Custom"]).toBe("value");
});
it("should support chaining for file streaming", function() {
var result = serializer
.setFileName("/path/to/file.pdf")
.withMime("application/pdf")
.andDelete(true);
expect(result.getFileName()).toBe("/path/to/file.pdf");
expect(result.getFileMime()).toBe("application/pdf");
expect(result.getDeleteFile()).toBeTrue();
});
});
});
}
}
+161
View File
@@ -0,0 +1,161 @@
component extends="testbox.system.BaseSpec" {
function run() {
describe("Factory", function() {
beforeEach(function() {
variables.factory = new core.factory();
});
describe("init()", function() {
it("should initialize with empty bean cache", function() {
expect(factory.beans).toBeStruct();
expect(structIsEmpty(factory.beans)).toBeTrue();
});
it("should initialize with empty transients cache", function() {
expect(factory.transients).toBeStruct();
expect(structIsEmpty(factory.transients)).toBeTrue();
});
it("should return this for method chaining", function() {
var result = factory.init();
expect(new tests.helpers.MetadataHelper().extendsClass(result, "factory")).toBeTrue();
});
});
describe("beanExists() / containsBean()", function() {
it("should return false for non-existent bean", function() {
expect(factory.beanExists("nonExistent")).toBeFalse();
expect(factory.containsBean("nonExistent")).toBeFalse();
});
it("should return true for cached bean", function() {
factory.beans["testBean"] = { name: "test" };
expect(factory.beanExists("testBean")).toBeTrue();
expect(factory.containsBean("testBean")).toBeTrue();
});
it("containsBean should be alias for beanExists", function() {
factory.beans["aliasTest"] = { name: "test" };
expect(factory.containsBean("aliasTest")).toBe(factory.beanExists("aliasTest"));
});
});
describe("transientExists()", function() {
it("should return false for non-existent transient", function() {
expect(factory.transientExists("nonExistent")).toBeFalse();
});
it("should return true for cached transient", function() {
factory.transients["testTransient"] = "some.component.path";
expect(factory.transientExists("testTransient")).toBeTrue();
});
});
describe("getBean()", function() {
it("should return cached bean if exists", function() {
var testObj = { name: "cached bean" };
factory.beans["testBean"] = testObj;
var result = factory.getBean("testBean");
expect(result.name).toBe("cached bean");
});
it("should throw error for non-existent bean", function() {
expect(function() {
factory.getBean("nonExistent");
}).toThrow("Taffy.Factory.BeanNotFound");
});
});
describe("getBeanList()", function() {
it("should return empty string when no beans", function() {
expect(factory.getBeanList()).toBe("");
});
it("should return bean names as comma-separated list", function() {
factory.beans["bean1"] = {};
factory.beans["bean2"] = {};
var list = factory.getBeanList();
expect(listLen(list)).toBe(2);
expect(listFindNoCase(list, "bean1")).toBeGT(0);
expect(listFindNoCase(list, "bean2")).toBeGT(0);
});
it("should include transients in the list", function() {
factory.beans["bean1"] = {};
factory.transients["transient1"] = "some.path";
var list = factory.getBeanList();
expect(listLen(list)).toBe(2);
expect(listFindNoCase(list, "transient1")).toBeGT(0);
});
});
describe("loadBeansFromPath()", function() {
it("should not throw error for non-existent path", function() {
// loadBeansFromPath should handle non-existent directories gracefully
expect(function() {
factory.loadBeansFromPath("/non/existent/path");
}).notToThrow();
});
it("should clear beans on full reload", function() {
factory.beans["existingBean"] = {};
var taffyRef = {
status: { skippedResources: [] },
beanList: ""
};
factory.loadBeansFromPath(
beanPath = "/non/existent/path",
resourcesPath = "resources",
resourcesBasePath = "",
isFullReload = true,
taffyRef = taffyRef
);
// After reload with non-existent path, beans should be cleared
expect(structIsEmpty(factory.beans)).toBeTrue();
});
});
describe("External bean factory integration", function() {
it("should accept external bean factory in init", function() {
var mockExternalFactory = {
containsBean: function(beanName) { return beanName == "externalBean"; },
getBean: function(beanName) { return { name: "external" }; }
};
factory.init(mockExternalFactory);
expect(structKeyExists(factory, "externalBeanFactory")).toBeTrue();
});
it("should check external factory for beans when configured", function() {
var mockExternalFactory = {
containsBean: function(beanName) { return beanName == "externalBean"; },
getBean: function(beanName) { return { name: "from external", source: "external" }; }
};
factory.init(mockExternalFactory);
// beanExists with includeExternal=true should find external beans
expect(factory.beanExists("externalBean", true, true)).toBeTrue();
});
});
});
}
}
@@ -0,0 +1,149 @@
component extends="testbox.system.BaseSpec" {
function run() {
describe("NativeJsonDeserializer", function() {
beforeEach(function() {
variables.deserializer = new core.nativeJsonDeserializer();
});
describe("inheritance", function() {
it("should extend baseDeserializer", function() {
expect(new tests.helpers.MetadataHelper().extendsClass(deserializer, "baseDeserializer")).toBeTrue();
});
it("should have getFromForm from parent", function() {
expect(deserializer).toHaveKey("getFromForm");
});
});
describe("getFromJson()", function() {
it("should parse simple JSON object", function() {
var body = '{"name":"John","age":30}';
var result = deserializer.getFromJson(body);
expect(result.name).toBe("John");
expect(result.age).toBe(30);
});
it("should parse nested JSON objects", function() {
var body = '{"user":{"name":"John","email":"john@example.com"},"active":true}';
var result = deserializer.getFromJson(body);
expect(result.user.name).toBe("John");
expect(result.user.email).toBe("john@example.com");
expect(result.active).toBeTrue();
});
it("should parse JSON with arrays", function() {
var body = '{"items":[1,2,3],"tags":["a","b","c"]}';
var result = deserializer.getFromJson(body);
expect(result.items).toBeArray();
expect(arrayLen(result.items)).toBe(3);
expect(result.tags[1]).toBe("a");
});
it("should handle boolean values", function() {
var body = '{"active":true,"deleted":false}';
var result = deserializer.getFromJson(body);
expect(result.active).toBeTrue();
expect(result.deleted).toBeFalse();
});
it("should handle null values", function() {
var body = '{"value":null,"other":"test"}';
var result = deserializer.getFromJson(body);
// Note: In Lucee, null values may not preserve the key in the struct
// We verify the deserializer doesn't throw an error and returns a struct
expect(result).toBeStruct();
expect(result.other).toBe("test");
});
it("should handle numeric values", function() {
var body = '{"integer":42,"decimal":3.14,"negative":-100}';
var result = deserializer.getFromJson(body);
expect(result.integer).toBe(42);
expect(result.decimal).toBe(3.14);
expect(result.negative).toBe(-100);
});
it("should handle empty object", function() {
var body = '{}';
var result = deserializer.getFromJson(body);
expect(result).toBeStruct();
expect(structIsEmpty(result)).toBeTrue();
});
it("should wrap non-struct JSON in _body key", function() {
// When JSON is an array at root level, wrap it
var body = '[1,2,3]';
var result = deserializer.getFromJson(body);
expect(structKeyExists(result, "_body")).toBeTrue();
expect(result._body).toBeArray();
});
it("should wrap primitive JSON values in _body key", function() {
var body = '"just a string"';
var result = deserializer.getFromJson(body);
expect(structKeyExists(result, "_body")).toBeTrue();
expect(result._body).toBe("just a string");
});
it("should handle complex nested structures", function() {
var body = '{
"users": [
{"id": 1, "name": "John", "roles": ["admin", "user"]},
{"id": 2, "name": "Jane", "roles": ["user"]}
],
"metadata": {
"total": 2,
"page": 1
}
}';
var result = deserializer.getFromJson(body);
expect(result.users).toBeArray();
expect(arrayLen(result.users)).toBe(2);
expect(result.users[1].roles).toBeArray();
expect(result.metadata.total).toBe(2);
});
it("should handle unicode characters", function() {
var body = '{"message":"Hello \u4e16\u754c"}';
var result = deserializer.getFromJson(body);
expect(structKeyExists(result, "message")).toBeTrue();
});
});
describe("taffy:mime metadata", function() {
it("should have taffy:mime for application/json", function() {
var metadata = getMetadata(deserializer);
var func = "";
for (var f in metadata.functions) {
if (f.name == "getFromJson") {
func = f;
break;
}
}
expect(func).notToBeEmpty();
var hasMime = structKeyExists(func, "taffy:mime")
|| structKeyExists(func, "taffy_mime");
expect(hasMime).toBeTrue();
// Check the actual mime types
var mimeValue = structKeyExists(func, "taffy:mime")
? func["taffy:mime"]
: func["taffy_mime"];
expect(mimeValue).toInclude("application/json");
});
});
});
}
}
@@ -0,0 +1,205 @@
component extends="testbox.system.BaseSpec" {
function beforeAll() {
// Initialize application scope with minimal settings
application._taffy = {
settings: {
serializer: "core.nativeJsonSerializer",
noDataSends204NoContent: false
},
factory: new core.factory(),
compat: {
queryToArray: "missing",
queryToStruct: "missing"
}
};
}
function run() {
describe("NativeJsonSerializer", function() {
beforeEach(function() {
variables.serializer = new core.nativeJsonSerializer();
});
describe("inheritance", function() {
it("should extend baseSerializer", function() {
expect(new tests.helpers.MetadataHelper().extendsClass(serializer, "baseSerializer")).toBeTrue();
});
it("should have all baseSerializer methods", function() {
expect(serializer).toHaveKey("setData");
expect(serializer).toHaveKey("getData");
expect(serializer).toHaveKey("withStatus");
expect(serializer).toHaveKey("withHeaders");
expect(serializer).toHaveKey("noData");
expect(serializer).toHaveKey("noContent");
});
});
describe("getAsJson()", function() {
it("should serialize simple string to JSON", function() {
serializer.setData("Hello World");
var result = serializer.getAsJson();
expect(result).toBe('"Hello World"');
});
it("should serialize struct to JSON", function() {
serializer.setData({ name: "Test", value: 123 });
var result = serializer.getAsJson();
var parsed = deserializeJSON(result);
expect(parsed.name).toBe("Test");
expect(parsed.value).toBe(123);
});
it("should serialize array to JSON", function() {
serializer.setData([1, 2, 3]);
var result = serializer.getAsJson();
var parsed = deserializeJSON(result);
expect(parsed).toBeArray();
expect(arrayLen(parsed)).toBe(3);
});
it("should serialize nested structures to JSON", function() {
serializer.setData({
user: {
name: "John",
roles: ["admin", "user"]
},
active: true
});
var result = serializer.getAsJson();
var parsed = deserializeJSON(result);
expect(parsed.user.name).toBe("John");
expect(parsed.user.roles).toBeArray();
expect(parsed.active).toBeTrue();
});
it("should serialize boolean values correctly", function() {
serializer.setData({ active: true, deleted: false });
var result = serializer.getAsJson();
var parsed = deserializeJSON(result);
expect(parsed.active).toBeTrue();
expect(parsed.deleted).toBeFalse();
});
it("should serialize null values", function() {
serializer.setData({ value: javacast("null", "") });
var result = serializer.getAsJson();
expect(result).toInclude("null");
});
it("should handle empty struct", function() {
serializer.setData({});
var result = serializer.getAsJson();
expect(result).toBe("{}");
});
it("should handle empty array", function() {
serializer.setData([]);
var result = serializer.getAsJson();
expect(result).toBe("[]");
});
it("should handle numeric values", function() {
serializer.setData({
integer: 42,
decimal: 3.14,
negative: -100
});
var result = serializer.getAsJson();
var parsed = deserializeJSON(result);
expect(parsed.integer).toBe(42);
expect(parsed.decimal).toBe(3.14);
expect(parsed.negative).toBe(-100);
});
});
describe("taffy:mime metadata", function() {
it("should have taffy:mime metadata for application/json", function() {
var metadata = getMetadata(serializer);
var getAsJsonFunc = "";
for (var func in metadata.functions) {
if (func.name == "getAsJson") {
getAsJsonFunc = func;
break;
}
}
expect(getAsJsonFunc).notToBeEmpty();
var hasMime = structKeyExists(getAsJsonFunc, "taffy:mime")
|| structKeyExists(getAsJsonFunc, "taffy_mime");
expect(hasMime).toBeTrue();
});
it("should be marked as default serializer", function() {
var metadata = getMetadata(serializer);
var getAsJsonFunc = "";
for (var func in metadata.functions) {
if (func.name == "getAsJson") {
getAsJsonFunc = func;
break;
}
}
var hasDefault = structKeyExists(getAsJsonFunc, "taffy:default")
|| structKeyExists(getAsJsonFunc, "taffy_default");
expect(hasDefault).toBeTrue();
});
});
describe("encode.string helper", function() {
it("should force numeric strings to serialize as strings", function() {
// The forceString function adds chr(2) which is then stripped by getAsJson
// This ensures numeric-looking strings serialize as strings, not numbers
var resource = new core.resource();
var forcedString = resource.forceString("12345");
// The chr(2) prefix forces CF to treat this as a string during serialization
expect(left(forcedString, 1)).toBe(chr(2));
// When serialized via nativeJsonSerializer, the chr(2) is stripped
serializer.setData({ value: forcedString });
var result = serializer.getAsJson();
// The value should be serialized as a string (with quotes), not a number
expect(result).toInclude('"');
});
});
describe("combined operations", function() {
it("should work with setData and status code", function() {
serializer.setData({ error: "Not found" }).withStatus(404);
var result = serializer.getAsJson();
var parsed = deserializeJSON(result);
expect(parsed.error).toBe("Not found");
expect(serializer.getStatus()).toBe(404);
});
it("should work with setData, status, and headers", function() {
serializer
.setData({ created: true })
.withStatus(201)
.withHeaders({ "Location": "/items/123" });
var result = serializer.getAsJson();
var parsed = deserializeJSON(result);
expect(parsed.created).toBeTrue();
expect(serializer.getStatus()).toBe(201);
expect(serializer.getHeaders()["Location"]).toBe("/items/123");
});
});
});
}
}
+132
View File
@@ -0,0 +1,132 @@
component extends="testbox.system.BaseSpec" {
function beforeAll() {
variables.gen = new core.openapi();
makePublic(variables.gen, "normalizePathTemplate");
makePublic(variables.gen, "cfmlTypeToSchema");
makePublic(variables.gen, "buildOperation");
}
function run() {
describe("openapi generator", function() {
describe("normalizePathTemplate", function() {
it("leaves simple paths untouched", function() {
expect(gen.normalizePathTemplate("/users", [])).toBe("/users");
});
it("leaves {name} tokens untouched", function() {
expect(gen.normalizePathTemplate("/users/{id}", ["id"])).toBe("/users/{id}");
});
it("strips {name:regex} down to {name}", function() {
expect(gen.normalizePathTemplate("/users/{id:[0-9]+}", ["id"])).toBe("/users/{id}");
});
it("strips greedy regex like {name:.+}", function() {
expect(gen.normalizePathTemplate("/files/{path:.+}", ["path"])).toBe("/files/{path}");
});
it("handles nested braces in token regexes", function() {
expect(gen.normalizePathTemplate("/a/{b:[a-z]+(?:42){1}}", ["b"])).toBe("/a/{b}");
});
it("rewrites multiple tokens in order", function() {
expect(gen.normalizePathTemplate("/events/{eventId:\d+}/attendees/{personId}", ["eventId", "personId"]))
.toBe("/events/{eventId}/attendees/{personId}");
});
});
describe("cfmlTypeToSchema", function() {
it("maps numeric to number", function() {
expect(gen.cfmlTypeToSchema("numeric")).toBe({ "type": "number" });
});
it("maps boolean to boolean", function() {
expect(gen.cfmlTypeToSchema("boolean")).toBe({ "type": "boolean" });
});
it("maps date to string/date-time", function() {
expect(gen.cfmlTypeToSchema("date")).toBe({ "type": "string", "format": "date-time" });
});
it("maps uuid to string/uuid", function() {
expect(gen.cfmlTypeToSchema("uuid")).toBe({ "type": "string", "format": "uuid" });
});
it("returns empty schema for 'any'", function() {
expect(gen.cfmlTypeToSchema("any")).toBe({});
});
it("returns empty schema for unset type", function() {
expect(gen.cfmlTypeToSchema("")).toBe({});
});
});
describe("buildOperation — path param handling", function() {
// minimal fixtures
var mkEndpoint = function(beanName, srcUri, tokens, methods) {
return { beanName: beanName, srcUri: srcUri, tokens: tokens, methods: methods };
};
var mkFunc = function(name, parameters = []) {
return { name: name, parameters: parameters };
};
it("backfills path tokens not declared as function args", function() {
var endpoint = mkEndpoint("MyResource", "/items/{id}", ["id"], { get: "get" });
var func = mkFunc("get", []); // function declares NO args
var op = gen.buildOperation("get", func, endpoint, {}, ["application/json"]);
expect(op).toHaveKey("parameters");
expect(arrayLen(op.parameters)).toBe(1);
expect(op.parameters[1].name).toBe("id");
expect(op.parameters[1]["in"]).toBe("path");
expect(op.parameters[1].required).toBeTrue();
});
it("uses token's original case when arg casing differs", function() {
var endpoint = mkEndpoint("AttendeesResource", "/attendees/{personId}", ["personId"], { put: "put" });
// simulate CFML metadata that lowercased the arg name
var func = mkFunc("put", [ { name: "personid", type: "numeric", required: true } ]);
var op = gen.buildOperation("put", func, endpoint, {}, ["application/json"]);
expect(op.parameters[1].name).toBe("personId");
expect(op.parameters[1]["in"]).toBe("path");
expect(op.parameters[1].schema).toBe({ "type": "number" });
});
it("puts non-token args for GET into query", function() {
var endpoint = mkEndpoint("ItemsResource", "/items", [], { get: "get" });
var func = mkFunc("get", [ { name: "limit", type: "numeric", required: false } ]);
var op = gen.buildOperation("get", func, endpoint, {}, ["application/json"]);
expect(op.parameters[1].name).toBe("limit");
expect(op.parameters[1]["in"]).toBe("query");
expect(op.parameters[1].required).toBeFalse();
});
it("puts non-token args for POST into requestBody with both json + form", function() {
var endpoint = mkEndpoint("ItemsResource", "/items", [], { post: "post" });
var func = mkFunc("post", [ { name: "name", type: "string", required: true } ]);
var op = gen.buildOperation("post", func, endpoint, {}, ["application/json"]);
expect(op).toHaveKey("requestBody");
expect(op.requestBody.content).toHaveKey("application/json");
expect(op.requestBody.content).toHaveKey("application/x-www-form-urlencoded");
expect(op.requestBody.content["application/json"].schema.properties).toHaveKey("name");
expect(op.requestBody.content["application/json"].schema.required).toBe(["name"]);
});
});
});
}
}
+207
View File
@@ -0,0 +1,207 @@
component extends="testbox.system.BaseSpec" {
function beforeAll() {
// Initialize application scope with minimal settings
application._taffy = {
settings: {
serializer: "core.nativeJsonSerializer",
noDataSends204NoContent: false
},
factory: new core.factory(),
compat: {
queryToArray: "missing",
queryToStruct: "missing"
}
};
// Create sample data fixture
variables.sampleData = new tests.fixtures.SampleData();
}
function run() {
describe("Resource", function() {
beforeEach(function() {
variables.resource = new core.resource();
});
describe("rep() / representationOf()", function() {
it("should return a serializer instance with data", function() {
var result = resource.rep({ message: "test" });
expect(new tests.helpers.MetadataHelper().extendsClass(result, "baseSerializer")).toBeTrue();
expect(result.getData().message).toBe("test");
});
it("rep() should be alias for representationOf()", function() {
var data = { id: 123 };
var rep1 = resource.rep(data);
// Both should return serializer instances with same data
expect(rep1.getData().id).toBe(123);
});
it("should work with simple string data", function() {
var result = resource.rep("Hello World");
expect(result.getData()).toBe("Hello World");
});
it("should work with array data", function() {
var result = resource.rep([1, 2, 3]);
expect(result.getData()).toBeArray();
expect(arrayLen(result.getData())).toBe(3);
});
it("should work with nested struct data", function() {
var data = {
user: { name: "John", age: 30 },
roles: ["admin", "user"]
};
var result = resource.rep(data);
expect(result.getData().user.name).toBe("John");
expect(result.getData().roles).toBeArray();
});
});
describe("encode.string()", function() {
it("should add control character prefix to force string serialization", function() {
// forceString is public (no access modifier), encode.string is just a reference to it
var result = resource.forceString("12345");
expect(left(result, 1)).toBe(chr(2));
});
it("should preserve the original value after prefix", function() {
var result = resource.forceString("12345");
expect(right(result, 5)).toBe("12345");
});
it("should work with numeric-looking strings", function() {
var result = resource.forceString("00123");
expect(len(result)).toBe(6); // chr(2) + "00123"
});
});
describe("qToArray() - internal query to array conversion", function() {
beforeEach(function() {
makePublic(resource, "qToArray");
});
it("should convert query to array of structs", function() {
var q = sampleData.getSampleQuery();
var result = resource.qToArray(q);
expect(result).toBeArray();
expect(arrayLen(result)).toBe(3);
});
it("should preserve column values in structs", function() {
var q = sampleData.getSampleQuery();
var result = resource.qToArray(q);
// Find the row with id=1
var found = false;
for (var row in result) {
if (row.id == 1) {
expect(row.name).toBe("John Doe");
expect(row.email).toBe("john@example.com");
found = true;
break;
}
}
expect(found).toBeTrue();
});
it("should handle empty query", function() {
var q = queryNew("id,name", "integer,varchar");
var result = resource.qToArray(q);
expect(result).toBeArray();
expect(arrayLen(result)).toBe(0);
});
it("should support callback function for row transformation", function() {
var q = sampleData.getSingleRowQuery();
var result = resource.qToArray(q, function(row) {
row.transformed = true;
return row;
});
expect(result[1].transformed).toBeTrue();
});
});
describe("qToStruct() - internal query to struct conversion", function() {
beforeEach(function() {
makePublic(resource, "qToStruct");
});
it("should convert single-row query to struct", function() {
var q = sampleData.getSingleRowQuery();
var result = resource.qToStruct(q);
expect(result).toBeStruct();
expect(result.id).toBe(1);
expect(result.name).toBe("Test User");
});
it("should throw error for multi-row query", function() {
var q = sampleData.getSampleQuery(); // Has 3 rows
expect(function() {
resource.qToStruct(q);
}).toThrow();
});
it("should handle query with all column types", function() {
var q = sampleData.getSingleRowQuery();
var result = resource.qToStruct(q);
expect(structKeyExists(result, "id")).toBeTrue();
expect(structKeyExists(result, "name")).toBeTrue();
expect(structKeyExists(result, "email")).toBeTrue();
});
it("should support callback function for value transformation", function() {
var q = sampleData.getSingleRowQuery();
var result = resource.qToStruct(q, function(colName, value) {
if (colName == "name") {
return uCase(value);
}
return value;
});
expect(result.name).toBe("TEST USER");
});
});
describe("method chaining with rep()", function() {
it("should support chaining with withStatus()", function() {
var result = resource.rep({ created: true }).withStatus(201);
expect(result.getStatus()).toBe(201);
expect(result.getData().created).toBeTrue();
});
it("should support chaining with withHeaders()", function() {
var result = resource.rep({ data: "test" })
.withHeaders({ "X-Custom": "value" });
expect(result.getHeaders()["X-Custom"]).toBe("value");
});
it("should support full fluent chain", function() {
var result = resource.rep({ id: 123 })
.withStatus(201, "Created")
.withHeaders({ "Location": "/items/123" });
expect(result.getData().id).toBe(123);
expect(result.getStatus()).toBe(201);
expect(result.getStatusText()).toBe("Created");
expect(result.getHeaders()["Location"]).toBe("/items/123");
});
});
});
}
}