Skip to content
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

Mongoose 8.x: Discriminator schema validation breaks in static validation #15075

Closed
2 tasks done
skrtheboss opened this issue Dec 4, 2024 · 2 comments · Fixed by #15124
Closed
2 tasks done

Mongoose 8.x: Discriminator schema validation breaks in static validation #15075

skrtheboss opened this issue Dec 4, 2024 · 2 comments · Fixed by #15124
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@skrtheboss
Copy link
Contributor

skrtheboss commented Dec 4, 2024

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

8.8.3 (also observed in 8.0.0)

Node.js version

20.17.0

MongoDB server version

7.0.15

Typescript version (if applicable)

No response

Description

There are significant inconsistencies in validation behavior between instance validation (new Model().validate()) and static validation (Model.validate()) when using discriminator schemas with nested document arrays:

Instance Validation

  • Circle: Passes validation ✓
  • Square: Fails validation as expected ✓

Static Validation

  • Circle: Fails validation with "radius is required" ✗
  • Square: Passes validation unexpectedly ✗

Regression?

This issue appears to have changed in behavior between Mongoose versions:

  • Mongoose 6.13.5: Both instance and static validation work consistently
  • Mongoose 7.8.3: Both instance and static validation work consistently
  • Mongoose 8.0.0: Inconsistent validation behavior emerges
  • Mongoose 8.8.3: Inconsistent validation behavior persists

Steps to Reproduce

const mongoose = require('mongoose');

// Interfaces (conceptual, for TypeScript-like clarity)
// interface Circle { kind: 'Circle'; radius: number; }
// interface PropertyPath { property: string; path: string; }
// interface Square { kind: 'Square'; propertyPaths: PropertyPath[]; }

async function runValidationTest() {
    // Create the base shape schema
    const shapeSchema = new mongoose.Schema({ name: String }, {
        discriminatorKey: 'kind',
        _id: false
    });

    // Main schema with shape array
    const schema = new mongoose.Schema({
        shape: [shapeSchema]
    });

    // Circle discriminator
    schema
        .path('shape')
        .discriminator('Circle', new mongoose.Schema({
            radius: {
                type: mongoose.Schema.Types.Number,
                required: true
            }
        }, { _id: false }));

    // PropertyPath schema for Square
    const propertyPathSchema = new mongoose.Schema({
        property: {
            type: mongoose.Schema.Types.String,
            required: true,
        },
        path: {
            type: mongoose.Schema.Types.String,
            required: true,
        }
    }, { _id: false });

    // Square discriminator
    schema
        .path('shape')
        .discriminator(
            'Square',
            new mongoose.Schema({
                propertyPaths: {
                    type: [propertyPathSchema],
                    required: true
                }
            }, { _id: false })
        );

    // Create the model
    const Model = mongoose.model('ShapeTest', schema);

    // Test cases
    const circle = { shape: [{ kind: 'Circle', radius: 5 }] };
    const square = { shape: [{ kind: 'Square', propertyPaths: [{}] }] };

    console.log("--- Instance Validation (new Model().validate()) ---");

    // Circle instance validation
    try {
        await new Model(circle).validate();
        console.log("Circle instance validation: PASSED ✓");
    } catch (error) {
        console.error("Circle instance validation: FAILED ✗", error);
    }

    // Square instance validation
    try {
        await new Model(square).validate();
        console.error("Square instance validation: UNEXPECTEDLY PASSED ✗");
    } catch (error) {
        console.log("Square instance validation: FAILED AS EXPECTED ✓");
        console.log("Validation Error:", error.message);
    }

    console.log("\n--- Static Validation (Model.validate()) ---");

    // Circle static validation
    try {
        await Model.validate(circle);
        console.log("Circle static validation: PASSED ✓");
    } catch (error) {
        console.error("Circle static validation: FAILED ✗", error);
    }

    // Square static validation
    try {
        await Model.validate(square);
        console.error("Square static validation: UNEXPECTEDLY PASSED ✗");
    } catch (error) {
        console.log("Square static validation: FAILED AS EXPECTED ✓");
        console.log("Validation Error:", error.message);
    }
}

// Run the test
runValidationTest().catch(console.error);

Expected Behavior

Both new Model().validate() and Model.validate() should:

  1. Successfully validate circle documents with a radius
  2. Fail validation for square documents missing required propertyPaths properties
@vkarpov15 vkarpov15 added this to the 8.9.1 milestone Dec 14, 2024
@vkarpov15 vkarpov15 added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Dec 14, 2024
vkarpov15 added a commit that referenced this issue Dec 16, 2024
fix(model): handle discriminators in castObject()
@vkarpov15
Copy link
Collaborator

Fixed by #15096

@skrtheboss
Copy link
Contributor Author

@vkarpov15 I am still experiencing inconsistency between static and instance validation.

If I modify the test case to change propertyPaths from an array to an object:

const square = { shape: [{ kind: 'Square', propertyPaths: {} }] };

Here are the results:

--- Instance Validation (new Model().validate()) ---
Circle instance validation: PASSED ✓
Square instance validation: FAILED AS EXPECTED ✓
Validation Error: ShapeTest validation failed: shape.0.propertyPaths.0.path: Path `path` is required., shape.0.propertyPaths.0.property: Path `property` is required.

--- Static Validation (Model.validate()) ---
Circle static validation: PASSED ✓
Square static validation: UNEXPECTEDLY PASSED ✗

The static validation is still incorrectly passing in this case.
Let me know if I should create a new issue. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
2 participants