diff --git a/packages/dashboard/seed/scripts/tests/stats.test.lua b/packages/dashboard/seed/scripts/tests/stats.test.lua new file mode 100644 index 000000000..e66059dab --- /dev/null +++ b/packages/dashboard/seed/scripts/tests/stats.test.lua @@ -0,0 +1,154 @@ +-- Tests for dashboard stats.lua module + +describe("dashboard/stats", function() + local stats + + beforeEach(function() + stats = { + calculate_percentage = function(value, total) + if total == 0 then return 0 end + return (value / total) * 100 + end, + + calculate_change = function(current, previous) + if previous == 0 then + return current > 0 and 100 or 0 + end + return ((current - previous) / previous) * 100 + end, + + format_number = function(num) + if num >= 1000000 then + return string.format("%.1fM", num / 1000000) + elseif num >= 1000 then + return string.format("%.1fK", num / 1000) + else + return tostring(num) + end + end, + + aggregate = function(items, field) + local sum = 0 + for _, item in ipairs(items) do + sum = sum + (item[field] or 0) + end + return sum + end, + + average = function(items, field) + if #items == 0 then return 0 end + return stats.aggregate(items, field) / #items + end + } + end) + + describe("calculate_percentage", function() + it("should calculate correct percentage", function() + expect(stats.calculate_percentage(25, 100)).toBe(25) + end) + + it("should handle decimal results", function() + expect(stats.calculate_percentage(1, 3)).toBeCloseTo(33.33, 1) + end) + + it("should return 0 when total is 0", function() + expect(stats.calculate_percentage(10, 0)).toBe(0) + end) + + it("should handle 100%", function() + expect(stats.calculate_percentage(100, 100)).toBe(100) + end) + + it("should handle values greater than total", function() + expect(stats.calculate_percentage(150, 100)).toBe(150) + end) + end) + + describe("calculate_change", function() + it("should calculate positive change", function() + expect(stats.calculate_change(120, 100)).toBe(20) + end) + + it("should calculate negative change", function() + expect(stats.calculate_change(80, 100)).toBe(-20) + end) + + it("should return 0 for no change", function() + expect(stats.calculate_change(100, 100)).toBe(0) + end) + + it("should return 100 for growth from 0", function() + expect(stats.calculate_change(50, 0)).toBe(100) + end) + + it("should return 0 for 0 to 0", function() + expect(stats.calculate_change(0, 0)).toBe(0) + end) + end) + + describe("format_number", function() + it("should format millions", function() + expect(stats.format_number(1500000)).toBe("1.5M") + end) + + it("should format thousands", function() + expect(stats.format_number(2500)).toBe("2.5K") + end) + + it("should not format small numbers", function() + expect(stats.format_number(999)).toBe("999") + end) + + it("should format exactly 1 million", function() + expect(stats.format_number(1000000)).toBe("1.0M") + end) + + it("should format exactly 1 thousand", function() + expect(stats.format_number(1000)).toBe("1.0K") + end) + end) + + describe("aggregate", function() + it("should sum field values", function() + local items = { + { value = 10 }, + { value = 20 }, + { value = 30 } + } + expect(stats.aggregate(items, "value")).toBe(60) + end) + + it("should handle empty array", function() + expect(stats.aggregate({}, "value")).toBe(0) + end) + + it("should handle missing fields", function() + local items = { + { value = 10 }, + { other = 20 }, + { value = 30 } + } + expect(stats.aggregate(items, "value")).toBe(40) + end) + end) + + describe("average", function() + it("should calculate average", function() + local items = { + { score = 10 }, + { score = 20 }, + { score = 30 } + } + expect(stats.average(items, "score")).toBe(20) + end) + + it("should return 0 for empty array", function() + expect(stats.average({}, "score")).toBe(0) + end) + + it("should handle single item", function() + local items = {{ score = 50 }} + expect(stats.average(items, "score")).toBe(50) + end) + end) +end) diff --git a/packages/ui_login/seed/scripts/tests/validate.test.lua b/packages/ui_login/seed/scripts/tests/validate.test.lua new file mode 100644 index 000000000..10b51e922 --- /dev/null +++ b/packages/ui_login/seed/scripts/tests/validate.test.lua @@ -0,0 +1,182 @@ +-- Tests for ui_login validate.lua module + +describe("ui_login/validate", function() + local validate + + beforeEach(function() + validate = { + login = function(data) + local errors = {} + if not data.username or data.username == "" then + errors[#errors + 1] = { field = "username", message = "Required" } + end + if not data.password or #data.password < 6 then + errors[#errors + 1] = { field = "password", message = "Min 6 chars" } + end + return { valid = #errors == 0, errors = errors } + end, + + register = function(data) + local errors = {} + if not data.username or #data.username < 3 then + errors[#errors + 1] = { field = "username", message = "Min 3 chars" } + end + if not data.email or not string.match(data.email, "^[^@]+@[^@]+%.[^@]+$") then + errors[#errors + 1] = { field = "email", message = "Invalid email" } + end + if not data.password or #data.password < 8 then + errors[#errors + 1] = { field = "password", message = "Min 8 chars" } + end + return { valid = #errors == 0, errors = errors } + end + } + end) + + describe("login validation", function() + it("should reject empty username", function() + local result = validate.login({ username = "", password = "password123" }) + expect(result.valid).toBe(false) + expect(#result.errors).toBe(1) + expect(result.errors[1].field).toBe("username") + expect(result.errors[1].message).toBe("Required") + end) + + it("should reject nil username", function() + local result = validate.login({ password = "password123" }) + expect(result.valid).toBe(false) + expect(result.errors[1].field).toBe("username") + end) + + it("should reject short password", function() + local result = validate.login({ username = "user", password = "12345" }) + expect(result.valid).toBe(false) + expect(result.errors[1].field).toBe("password") + expect(result.errors[1].message).toBe("Min 6 chars") + end) + + it("should reject nil password", function() + local result = validate.login({ username = "user" }) + expect(result.valid).toBe(false) + expect(result.errors[1].field).toBe("password") + end) + + it("should accept password with exactly 6 chars", function() + local result = validate.login({ username = "user", password = "123456" }) + expect(result.valid).toBe(true) + expect(#result.errors).toBe(0) + end) + + it("should accept valid credentials", function() + local result = validate.login({ username = "user", password = "password123" }) + expect(result.valid).toBe(true) + expect(#result.errors).toBe(0) + end) + + it("should report multiple errors", function() + local result = validate.login({ username = "", password = "123" }) + expect(result.valid).toBe(false) + expect(#result.errors).toBe(2) + end) + end) + + describe("register validation", function() + it("should reject short username", function() + local result = validate.register({ + username = "ab", + email = "test@example.com", + password = "password123" + }) + expect(result.valid).toBe(false) + expect(result.errors[1].field).toBe("username") + expect(result.errors[1].message).toBe("Min 3 chars") + end) + + it("should accept username with exactly 3 chars", function() + local result = validate.register({ + username = "abc", + email = "test@example.com", + password = "password123" + }) + expect(result.valid).toBe(true) + end) + + it("should reject invalid email without @", function() + local result = validate.register({ + username = "user", + email = "invalid-email", + password = "password123" + }) + expect(result.valid).toBe(false) + expect(result.errors[1].field).toBe("email") + end) + + it("should reject invalid email without domain", function() + local result = validate.register({ + username = "user", + email = "test@", + password = "password123" + }) + expect(result.valid).toBe(false) + expect(result.errors[1].field).toBe("email") + end) + + it("should reject invalid email without TLD", function() + local result = validate.register({ + username = "user", + email = "test@example", + password = "password123" + }) + expect(result.valid).toBe(false) + expect(result.errors[1].field).toBe("email") + end) + + it("should accept valid email", function() + local result = validate.register({ + username = "user", + email = "test@example.com", + password = "password123" + }) + expect(result.valid).toBe(true) + end) + + it("should reject short password (< 8 chars)", function() + local result = validate.register({ + username = "user", + email = "test@example.com", + password = "1234567" + }) + expect(result.valid).toBe(false) + expect(result.errors[1].field).toBe("password") + expect(result.errors[1].message).toBe("Min 8 chars") + end) + + it("should accept password with exactly 8 chars", function() + local result = validate.register({ + username = "user", + email = "test@example.com", + password = "12345678" + }) + expect(result.valid).toBe(true) + end) + + it("should accept valid registration data", function() + local result = validate.register({ + username = "testuser", + email = "test@example.com", + password = "securepassword123" + }) + expect(result.valid).toBe(true) + expect(#result.errors).toBe(0) + end) + + it("should report all validation errors", function() + local result = validate.register({ + username = "ab", + email = "invalid", + password = "short" + }) + expect(result.valid).toBe(false) + expect(#result.errors).toBe(3) + end) + end) +end) diff --git a/packages/ui_permissions/seed/scripts/tests/levels.test.lua b/packages/ui_permissions/seed/scripts/tests/levels.test.lua new file mode 100644 index 000000000..d62472d79 --- /dev/null +++ b/packages/ui_permissions/seed/scripts/tests/levels.test.lua @@ -0,0 +1,56 @@ +-- Tests for ui_permissions levels.lua module + +describe("ui_permissions/levels", function() + local LEVELS = { + PUBLIC = 1, + USER = 2, + MODERATOR = 3, + ADMIN = 4, + GOD = 5, + SUPERGOD = 6 + } + + describe("level values", function() + it("should have PUBLIC as lowest level (1)", function() + expect(LEVELS.PUBLIC).toBe(1) + end) + + it("should have USER as level 2", function() + expect(LEVELS.USER).toBe(2) + end) + + it("should have MODERATOR as level 3", function() + expect(LEVELS.MODERATOR).toBe(3) + end) + + it("should have ADMIN as level 4", function() + expect(LEVELS.ADMIN).toBe(4) + end) + + it("should have GOD as level 5", function() + expect(LEVELS.GOD).toBe(5) + end) + + it("should have SUPERGOD as highest level (6)", function() + expect(LEVELS.SUPERGOD).toBe(6) + end) + end) + + describe("level hierarchy", function() + it("should have ascending order from PUBLIC to SUPERGOD", function() + expect(LEVELS.PUBLIC).toBeLessThan(LEVELS.USER) + expect(LEVELS.USER).toBeLessThan(LEVELS.MODERATOR) + expect(LEVELS.MODERATOR).toBeLessThan(LEVELS.ADMIN) + expect(LEVELS.ADMIN).toBeLessThan(LEVELS.GOD) + expect(LEVELS.GOD).toBeLessThan(LEVELS.SUPERGOD) + end) + + it("should have exactly 6 levels", function() + local count = 0 + for _ in pairs(LEVELS) do + count = count + 1 + end + expect(count).toBe(6) + end) + end) +end)