-
Notifications
You must be signed in to change notification settings - Fork 2
Analyze staging environment errors on Railway #7
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,12 +2,39 @@ | |
| * TypeBox Validation Edge Case Tests | ||
| * Tests validation schemas across all routes | ||
| * NO MOCKS - Real validation behavior | ||
| * | ||
| * NOTE: TypeBox's Value.Check does NOT validate string formats (email, uuid, uri, date-time) | ||
| * by default. Format validation requires explicit format registration or use of TypeCompiler. | ||
| * Tests here focus on structural validation that works out of the box. | ||
| */ | ||
|
|
||
| import { describe, it, expect } from "bun:test"; | ||
| import { Type as t, TSchema } from "@sinclair/typebox"; | ||
| import { Type as t, TSchema, FormatRegistry } from "@sinclair/typebox"; | ||
| import { Value } from "@sinclair/typebox/value"; | ||
|
|
||
| // Register common format validators for testing | ||
| // These are the same patterns Elysia uses internally | ||
| FormatRegistry.Set("email", (value) => | ||
| /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value), | ||
| ); | ||
| FormatRegistry.Set("uuid", (value) => | ||
| /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test( | ||
| value, | ||
| ), | ||
| ); | ||
| FormatRegistry.Set("uri", (value) => { | ||
| try { | ||
| new URL(value); | ||
| return true; | ||
| } catch { | ||
| return false; | ||
| } | ||
| }); | ||
| FormatRegistry.Set("date-time", (value) => { | ||
| const date = new Date(value); | ||
| return !isNaN(date.getTime()) && value.includes("T"); | ||
| }); | ||
|
Comment on lines
+17
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Format Validators May Not Fully Cover Edge CasesThe custom format validators for Recommendation: Consider using more robust validation libraries (such as |
||
|
|
||
| describe("TypeBox Validation Edge Cases", () => { | ||
| describe("String Validation", () => { | ||
| it("should reject empty strings when required", () => { | ||
|
|
@@ -17,9 +44,12 @@ describe("TypeBox Validation Edge Cases", () => { | |
| expect(result).toBe(false); | ||
| }); | ||
|
|
||
| it("should accept empty strings when optional", () => { | ||
| const schema = t.Optional(t.String()); | ||
| const result = Value.Check(schema, undefined); | ||
| it("should accept undefined when optional in object context", () => { | ||
| // Optional types work in the context of object schemas | ||
| const schema = t.Object({ | ||
| name: t.Optional(t.String()), | ||
| }); | ||
| const result = Value.Check(schema, {}); | ||
|
|
||
| expect(result).toBe(true); | ||
| }); | ||
|
|
@@ -331,12 +361,15 @@ describe("TypeBox Validation Edge Cases", () => { | |
| expect(Value.Check(schema, undefined)).toBe(false); | ||
| }); | ||
|
|
||
| it("should handle optional types", () => { | ||
| const schema = t.Optional(t.String()); | ||
| it("should handle optional types in object context", () => { | ||
| // Optional works correctly within object schemas | ||
| const schema = t.Object({ | ||
| name: t.Optional(t.String()), | ||
| }); | ||
|
|
||
| expect(Value.Check(schema, "value")).toBe(true); | ||
| expect(Value.Check(schema, undefined)).toBe(true); | ||
| expect(Value.Check(schema, null)).toBe(false); | ||
| expect(Value.Check(schema, { name: "value" })).toBe(true); | ||
| expect(Value.Check(schema, {})).toBe(true); | ||
| expect(Value.Check(schema, { name: null })).toBe(false); | ||
| }); | ||
|
Comment on lines
+364
to
373
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional and Nullable Type Handling Is Version-DependentThe tests for optional and nullable types in object context (lines 364-373) are correct for the current TypeBox behavior, but TypeBox's handling of Recommendation: Monitor TypeBox release notes for changes in optional/nullable semantics and update these tests accordingly to ensure continued correctness. |
||
| }); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,7 @@ export const projectsRoutes = new Elysia({ | |
| const project = await projectService.createProject({ | ||
| name: body.name, | ||
| description: body.description, | ||
| ownerId: user?.id || null, // Single-team: ownerId is optional | ||
| ownerId: user?.id || "", // Single-team: ownerId is optional | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ambiguous Ownership Handling Passing ownerId: user?.id ?? nullThis approach makes the intent clearer and avoids potential issues with empty string values.
Dexploarer marked this conversation as resolved.
Show resolved
Hide resolved
Dexploarer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| settings: body.settings, | ||
| metadata: body.metadata, | ||
|
Comment on lines
37
to
38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lack of Validation for Arbitrary Object Fields The |
||
| }); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,14 +87,13 @@ export const ContentPreviewCard: React.FC<ContentPreviewCardProps> = ({ | |
|
|
||
| try { | ||
| setIsGeneratingPortrait(true); | ||
| const result = await api.api.content["generate-npc-portrait"].post( | ||
| { | ||
| npcName: npc.name, | ||
| archetype: npc.archetype, | ||
| appearance: npc.appearance.description, | ||
| personality: npc.personality.traits.join(", "), | ||
| }, | ||
| ); | ||
| const result = await api.api.content["generate-npc-portrait"].post({ | ||
| npcName: npc.name, | ||
| entityId: content.id!, | ||
| archetype: npc.archetype, | ||
| appearance: npc.appearance.description, | ||
| personality: npc.personality.traits.join(", "), | ||
| }); | ||
|
|
||
| if (result.error) { | ||
| throw new Error( | ||
|
Comment on lines
98
to
99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Issue: Error Handling RobustnessThe error handling in the portrait generation logic assumes that Recommendation: if (result.error) {
const errorMsg = result.error.value?.message || result.error.value?.summary || JSON.stringify(result.error) || "Failed to generate portrait";
throw new Error(errorMsg);
} |
||
|
|
@@ -224,14 +223,12 @@ export const ContentPreviewCard: React.FC<ContentPreviewCardProps> = ({ | |
| const base64data = reader.result as string; | ||
| const base64Image = base64data.split(",")[1]; // Remove data:image/png;base64, prefix | ||
|
|
||
|
Comment on lines
224
to
225
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Issue: Base64 Extraction SafetyThe code assumes that Recommendation: const parts = base64data.split(",");
if (parts.length < 2 || !parts[1]) {
notify.error("Failed to extract base64 image data");
setIsSavingPortrait(false);
return;
}
const base64Image = parts[1]; |
||
| // Save to backend | ||
| const result = await api.api.content.media["save-portrait"].post( | ||
| { | ||
| entityType: "npc", | ||
| entityId: content.id!, | ||
| imageData: base64Image, | ||
| }, | ||
| ); | ||
| // Save to backend - use imageUrl with data URL format | ||
| const result = await api.api.content.media["save-portrait"].post({ | ||
| entityType: "npc", | ||
| entityId: content.id!, | ||
| imageUrl: `data:image/png;base64,${base64Image}`, | ||
| }); | ||
|
|
||
| if (result.error) { | ||
| throw new Error( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue: Potential Resource Leak in
runLoadThe use of
autocannon.track(instance)within the promise does not guarantee that the instance is properly cleaned up after the test completes. If the instance is not destroyed or closed, it may lead to resource exhaustion, especially when running multiple tests in sequence or under high concurrency.Recommendation:
Ensure that the autocannon instance is properly destroyed after the test completes. You can do this by calling
instance.stop()or similar cleanup logic in the callback, regardless of success or error:This will help prevent resource leaks and improve test reliability.