Skip to content

Conversation

@PrayagCodes
Copy link

@PrayagCodes PrayagCodes commented Nov 25, 2025

Summary

Fixes a critical bug in PuterHomepageService where attempting to validate invalid social media image URLs caused a runtime error (TypeError: Assignment to constant variable), preventing the homepage from loading when invalid URLs were configured.

Problem

The service was attempting to reassign a const variable (social_media_image) during validation checks. In JavaScript, const variables cannot be reassigned after their initial declaration, causing a runtime error that prevented the homepage from loading.

Root Cause

  • social_media_image was destructured as a const variable (line 175)
  • Validation logic attempted to reassign it to null on lines 197 and 202
  • This violated JavaScript's const immutability rules

Impact

  • Homepage failed to load when invalid social media image URLs were configured
  • Runtime error: TypeError: Assignment to constant variable
  • Degraded user experience for apps with invalid metadata

Solution

Replaced the const reassignment pattern with a mutable variable approach:

  1. Created a new let variable (validated_social_media_image) initialized with the original value
  2. Updated validation logic to modify the mutable variable instead of attempting to reassign the const
  3. Used the validated variable for the final URL assignment

Changes Made

File: src/backend/src/services/PuterHomepageService.js

  • Added mutable variable: let validated_social_media_image = social_media_image; (line 195)
  • Updated URL validation check to use mutable variable (lines 197-200)
  • Updated extension validation check to use mutable variable (lines 202-205)
  • Updated final URL assignment to use validated variable (line 208)

Validation Logic

The service now performs two validation checks:

  1. URL Format Validation: Ensures the URL is a valid absolute URL using is_valid_url() helper

    • Rejects: malformed URLs, relative paths, invalid protocols
    • Accepts: URLs starting with http:// or https://
  2. File Extension Validation: Ensures the URL ends with a valid image extension

    • Valid extensions: .png, .jpg, .jpeg, .gif, .webp

If either validation fails, validated_social_media_image is set to null, which triggers the fallback to the default image (${asset_dir}/images/screenshot.png).

Testing

Automated Tests

Created comprehensive test suite (test-bug-reproduction.cjs) covering:

  • Invalid file extensions (.txt, .html)
  • Malformed URLs
  • Relative paths
  • Valid URLs with all supported extensions
  • Edge cases (null, undefined, empty string)

All 13 test cases pass successfully.

Manual Testing

Verified the following scenarios:

  1. Valid URL: https://example.com/image.png - Uses provided URL correctly
  2. Invalid Extension: https://example.com/image.txt - Falls back to default image
  3. Malformed URL: not-a-valid-url - Falls back to default image
  4. Null/Undefined: Falls back to default image

All scenarios load successfully without runtime errors.

Acceptance Criteria

  • The validation logic does not attempt to reassign const variables
  • Invalid URLs (malformed or non-absolute) are properly detected and handled
  • URLs without valid image extensions are properly detected and handled
  • When validation fails, the service falls back to the default social media image
  • The homepage loads successfully regardless of whether the configured social media image URL is valid or invalid

Related Files

  • src/backend/src/services/PuterHomepageService.js - Main fix
  • test-bug-reproduction.cjs - Test suite (not committed)

Breaking Changes

None. This is a bug fix that maintains backward compatibility.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Comments added for complex logic
  • Documentation updated
  • No new warnings generated
  • Tests added/updated and passing
  • Manual testing completed

The PuterHomepageService was attempting to reassign a const variable (social_media_image) during validation, causing a runtime error that prevented the homepage from loading when invalid URLs were configured.

Changes:

- Replace const reassignment with mutable variable (validated_social_media_image)

- Update URL validation logic to use mutable variable

- Update extension validation logic to use mutable variable

- Maintain fallback to default image when validation fails

The fix ensures that:

- Invalid URLs (malformed or non-absolute) are properly detected

- URLs without valid image extensions are properly detected

- Service gracefully falls back to default image on validation failure

- Homepage loads successfully regardless of URL validity

Fixes the TypeError: Assignment to constant variable error that occurred at lines 197 and 202 when validating social media image URLs.
@PrayagCodes
Copy link
Author

Unit tests script

/**
 * Test script to verify the fix for PuterHomepageService social media image validation bug
 * 
 * This test verifies that:
 * 1. No runtime errors occur when validating invalid URLs
 * 2. Invalid URLs are properly detected and fallback to default image
 * 3. Valid URLs are used correctly
 * 4. Edge cases (null, undefined, empty) are handled
 * 5. The homepage loads successfully regardless of URL validity
 */

const { PuterHomepageService } = require('./src/backend/src/services/PuterHomepageService');

// Create a minimal service instance with required properties
// We use Object.create to avoid calling the constructor which requires service resources
const service = Object.create(PuterHomepageService.prototype);

// Initialize minimal properties needed by generate_puter_page_html
service.service_scripts = [];
service.gui_params = {};
service.require = require; // Use Node's require function

// Helper function to extract social media image URL from HTML
function extractSocialMediaImageUrl(html, env = 'dev') {
    const assetDir = env === 'dev' ? '/src' : '/dist';
    const defaultImage = `${assetDir}/images/screenshot.png`;
    
    // Check for og:image meta tag
    const ogImageMatch = html.match(/<meta property="og:image" content="([^"]+)">/);
    if (ogImageMatch) {
        return ogImageMatch[1];
    }
    
    // Check for twitter:image meta tag
    const twitterImageMatch = html.match(/<meta name="twitter:image" content="([^"]+)">/);
    if (twitterImageMatch) {
        return twitterImageMatch[1];
    }
    
    return null;
}

// Common test parameters
const baseParams = {
    env: 'dev',
    manifest: {},
    gui_path: '/gui',
    use_bundled_gui: false,
    app_origin: 'https://example.com',
    api_origin: 'https://api.example.com',
    launch_options: {},
    gui_params: {}
};

// Test cases
const testCases = [
    // Invalid URLs - should fallback to default
    {
        name: "Invalid file extension (.txt)",
        social_media_image: "https://example.com/image.txt",
        shouldFallback: true,
        description: "URL with invalid extension should fallback to default"
    },
    {
        name: "Malformed URL",
        social_media_image: "not-a-valid-url",
        shouldFallback: true,
        description: "Malformed URL should be rejected and fallback to default"
    },
    {
        name: "Valid URL with invalid extension (.html)",
        social_media_image: "https://example.com/image.html",
        shouldFallback: true,
        description: "Valid URL with invalid extension should fallback to default"
    },
    {
        name: "Relative path",
        social_media_image: "/images/test.png",
        shouldFallback: true,
        description: "Relative path should be rejected and fallback to default"
    },
    // Valid URLs - should be used as-is
    {
        name: "Valid URL with .png extension",
        social_media_image: "https://example.com/image.png",
        shouldFallback: false,
        expectedUrl: "https://example.com/image.png",
        description: "Valid URL with .png should be used"
    },
    {
        name: "Valid URL with .jpg extension",
        social_media_image: "https://example.com/image.jpg",
        shouldFallback: false,
        expectedUrl: "https://example.com/image.jpg",
        description: "Valid URL with .jpg should be used"
    },
    {
        name: "Valid URL with .jpeg extension",
        social_media_image: "https://example.com/image.jpeg",
        shouldFallback: false,
        expectedUrl: "https://example.com/image.jpeg",
        description: "Valid URL with .jpeg should be used"
    },
    {
        name: "Valid URL with .gif extension",
        social_media_image: "https://example.com/image.gif",
        shouldFallback: false,
        expectedUrl: "https://example.com/image.gif",
        description: "Valid URL with .gif should be used"
    },
    {
        name: "Valid URL with .webp extension",
        social_media_image: "https://example.com/image.webp",
        shouldFallback: false,
        expectedUrl: "https://example.com/image.webp",
        description: "Valid URL with .webp should be used"
    },
    {
        name: "Valid URL with uppercase extension (.PNG)",
        social_media_image: "https://example.com/image.PNG",
        shouldFallback: false,
        expectedUrl: "https://example.com/image.PNG",
        description: "Valid URL with uppercase extension should be used (case-insensitive check)"
    },
    // Edge cases - should fallback to default
    {
        name: "null value",
        social_media_image: null,
        shouldFallback: true,
        description: "null should fallback to default"
    },
    {
        name: "undefined value",
        social_media_image: undefined,
        shouldFallback: true,
        description: "undefined should fallback to default"
    },
    {
        name: "Empty string",
        social_media_image: "",
        shouldFallback: true,
        description: "Empty string should fallback to default"
    }
];

console.log("=".repeat(70));
console.log("Testing PuterHomepageService Social Media Image Validation Fix");
console.log("=".repeat(70));
console.log("\nThis test verifies that:");
console.log("1. No runtime errors occur (no TypeError: Assignment to constant variable)");
console.log("2. Invalid URLs are detected and fallback to default image");
console.log("3. Valid URLs are used correctly in the HTML output");
console.log("4. Edge cases (null, undefined, empty) are handled gracefully");
console.log("5. Homepage loads successfully regardless of URL validity");
console.log("=".repeat(70));
console.log();

let testsPassed = 0;
let testsFailed = 0;
const failures = [];

// Run each test case
testCases.forEach((testCase, index) => {
    console.log(`Test ${index + 1}: ${testCase.name}`);
    console.log(`  Description: ${testCase.description}`);
    console.log(`  Input: social_media_image = ${JSON.stringify(testCase.social_media_image)}`);
    
    let testPassed = true;
    let errorMessage = null;
    
    try {
        const meta = {
            title: "Test App",
            description: "Test description",
            short_description: "Test short description",
            company: "Test Company",
            canonical_url: "https://example.com",
            social_media_image: testCase.social_media_image
        };

        const html = service.generate_puter_page_html({
            ...baseParams,
            meta: meta
        });

        // Verify no errors were thrown
        if (!html || typeof html !== 'string') {
            testPassed = false;
            errorMessage = "HTML output is invalid";
        } else {
            // Extract the social media image URL from HTML
            const imageUrl = extractSocialMediaImageUrl(html, baseParams.env);
            const expectedDefaultUrl = `${baseParams.env === 'dev' ? '/src' : '/dist'}/images/screenshot.png`;
            
            if (testCase.shouldFallback) {
                // Should fallback to default
                if (imageUrl !== expectedDefaultUrl) {
                    testPassed = false;
                    errorMessage = `Expected fallback to default image (${expectedDefaultUrl}), but got: ${imageUrl}`;
                } else {
                    console.log(`  ✅ Correctly fell back to default image: ${imageUrl}`);
                }
            } else {
                // Should use the provided URL
                if (imageUrl !== testCase.expectedUrl) {
                    testPassed = false;
                    errorMessage = `Expected URL (${testCase.expectedUrl}), but got: ${imageUrl}`;
                } else {
                    console.log(`  ✅ Correctly used provided URL: ${imageUrl}`);
                }
            }
        }
        
        if (testPassed) {
            console.log("  ✅ PASSED: No errors, correct behavior");
            testsPassed++;
        } else {
            console.log(`  ❌ FAILED: ${errorMessage}`);
            testsFailed++;
            failures.push({
                test: testCase.name,
                error: errorMessage
            });
        }
    } catch (error) {
        testPassed = false;
        const errorMsg = error.message || String(error);
        console.log(`  ❌ FAILED: Unexpected error thrown`);
        console.log(`     Error Type: ${error.constructor.name}`);
        console.log(`     Error Message: ${errorMsg}`);
        
        // Check if it's the bug we're trying to fix
        if (errorMsg.includes("Assignment to constant variable")) {
            errorMessage = "BUG STILL EXISTS: TypeError: Assignment to constant variable";
        } else {
            errorMessage = `Unexpected error: ${errorMsg}`;
        }
        
        testsFailed++;
        failures.push({
            test: testCase.name,
            error: errorMessage
        });
    }
    console.log();
});

// Summary
console.log("=".repeat(70));
console.log("Test Summary");
console.log("=".repeat(70));
console.log(`Total Tests: ${testCases.length}`);
console.log(`✅ Passed: ${testsPassed}`);
console.log(`❌ Failed: ${testsFailed}`);
console.log();

if (testsFailed > 0) {
    console.log("Failed Tests:");
    failures.forEach((failure, index) => {
        console.log(`  ${index + 1}. ${failure.test}: ${failure.error}`);
    });
    console.log();
}

if (testsPassed === testCases.length) {
    console.log("✅ ALL TESTS PASSED!");
    console.log("\nThe fix is working correctly:");
    console.log("✓ No runtime errors occur");
    console.log("✓ Invalid URLs are properly detected and handled");
    console.log("✓ Valid URLs are used correctly");
    console.log("✓ Edge cases are handled gracefully");
    console.log("✓ Homepage loads successfully regardless of URL validity");
    console.log("\nAll acceptance criteria and expected behavior requirements are met!");
} else {
    console.log("❌ Some tests failed. Please review the failures above.");
}

console.log("=".repeat(70));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants