Unexpected test results in Typescript Unit Tests when using objects api

I am building some complex object set logic where I need to walk a tree of objects (dataset definitions) and get the connected entities (prices) in a coalesce-style so that in the end I dont have duplicates. Since the coalesce logic uses its own key definition / criteria of what is a duplicate, I need to test the logic. For this reason, I use the Objects.create() api and also .set() on the respective relations to create my objects with linked prices, and also determine the hierarchy between the stub objects, all according to the documentation: Functions • Unit testing • Stub object searches and aggregations • Palantir

This is the .getDataSetHierarchy method:

    public getDatasetHierarchy(dataset: Dataset): Dataset[] {
        const parent = dataset.parentDataset.get()
        if (!parent){
            return [dataset]
        }
        const outerParents = this.getDatasetHierarchy(parent)
        const concat = [dataset].concat(outerParents)
        return concat
    }

This is how my stubs factory:

async function stubsFactory() {
    const helpers = new TestHelpers()
        
    const parentDataSet = Objects.create().dataset("parent")
    parentDataSet.rid = "rid.p1"
    const childDataSet = Objects.create().dataset("child")
    childDataSet.rid = "rid.c1"
    childDataSet.parentDatasetKey = "parent"
    childDataSet.parentDataset.set(parentDataSet)
    
    const prices_ok = await helpers.readCsvAsObjects<MockPriceRow>(path.join(__dirname, 'mock_data', "selected_prices_ok.csv"))
    const pricesParent = prices_ok.filter(price => price.datasetKey === "parent").map(price => createConsolidatedPrice(price))
    const pricesChild = prices_ok.filter(price => price.datasetKey === "child").map(price => createConsolidatedPrice(price))
    
    pricesChild.forEach(price => price.dataset.set(childDataSet))
    pricesParent.forEach(price => price.dataset.set(parentDataSet))
    // console.log(pricesParent)

    return { parentDataSet, childDataSet }
}

Unfortunately, this test has some unexpected assertion failures:

    test("test get DatasetHierarchy", async () => {
        const { parentDataSet, childDataSet } = await stubsFactory()
        
        childDataSet.parentDataset.set(parentDataSet)
        const dc = new DataSetMethods()
        const allDatasets = dc.getDatasetHierarchy(childDataSet)
        
        expect(allDatasets[0].parentDataset.get()).toBeDefined()
        expect(allDatasets[0].prices.all()).toBeDefined()
        expect(allDatasets[0].prices.all().length).toEqual(1)
        expect(allDatasets[1].parentDataset.get()).toBeUndefined()
        expect(allDatasets[1].prices.all()).toBeDefined()

        expect(parentDataSet.prices.all().length).toEqual(2) // passes
        expect(allDatasets[1].prices.all().length).toEqual(2) // fails
        expect(childDataSet.parentDataset.get()).toBe(parentDataSet) // --> rid does not match
    })

The thing is: The method works in the ontology as expected which I figured out via e2e testing! I am primarily worried about the lines

        expect(parentDataSet.prices.all().length).toEqual(2) // passes
        expect(allDatasets[1].prices.all().length).toEqual(2) // fails

They should both return the same result and I cant come up with a reason why this may not be the case… Could it be that if you .set() and object in a test environment, it does not create a deep copy but only a shallow copy of the object expect(childDataSet.parentDataset.get()).toBe(parentDataSet) // --> rid does not match returns a failure since te two objects are not exactly the same (missing rid) and I assume the linked prices are also missing.

I know that ontology edits take only place after the function returns. However, this will not happen in a test scenario and if there is no other way to stub ontology objects like I do and to simulate the ontology in this way, I seriously doubt the value of this test environment :frowning:

Ps.: I changed some names here and there to make it more general for obvious reasons. Hopefully, I did not introduce more bugs :wink:

Interesting problem!

Have you tried running the unit tests that verify whether your code is making Ontology edits?
https://www.palantir.com/docs/foundry/functions/unit-test-ontology-edits/

Could also be interesting spitting out some property values, to see what’s going on?

Hi, thank you for the suggestion!

I tried it out and it gets worse!

i built this unit test:

    test("verify ontology stubs edits", async ()=>{
        const { parentDataSet, childDataSet } = await stubsFactory()

        verifyOntologyEditFunction(() => stubsFactory())
            .createsObject({
                objectType: Dataset,
                properties: {
                    name: "child"
                },
            })
            .addsLink(edits => ({
                link: childDataSet.parentDataset,
                linkedObject: edits.createdObjects.byObjectType(Dataset)[0],
            }))
    })

And during regression testing, other tests now started to fail depending on the order of the unit tests. It somewhat indicates that either the stubsFactory is not enough to separate the tests, or the the verifyOntologyEditFunction() accidentally connects the previously isolated tests. :open_mouth:

I now receive a SafeError:

SafeError: Object already exists
    at UserCausedError.SafeError [as constructor] (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/node_modules/@foundry/witchcraft-logging-api/src/args/safeError.ts:26:9)
    at new UserCausedError (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/node_modules/@foundry/functions-typescript-runtime-isolate-api/src/UserCausedError.ts:21:5)
    at Object.objectAlreadyExists (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/node_modules/@foundry/functions-typescript-runtime-storage/src/Errors.ts:45:12)
    at PrimaryKeyedObjectsStore.newObject (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/node_modules/@foundry/functions-typescript-runtime-storage/src/objects/PrimaryKeyedObjectsStore.ts:47:26)
    at InMemoryObjectsStore.newObject (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/node_modules/@foundry/functions-typescript-runtime-storage/src/objects/InMemoryObjectsStore.ts:57:47)
    at AnnotatingObjectStorageProvider.createObject (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/node_modules/@foundry/functions-typescript-runtime-storage/src/DefaultObjectStorageProvider.ts:160:27)
    at AnnotatingObjectStorageProvider.createObject (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/node_modules/@foundry/functions-testing-lib/src/storage/AnnotatingObjectStorageProvider.ts:51:37)
    at ObjectCreator<redacted>.consolidatedDataset (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/node_modules/@foundry/ontology-api/ontology-28301fe2-2d78-4203-a25c-9e36560ce542/ontology-<redacted>/ObjectCreatorImpl.js:12:48)
    at stubsFactory (/scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/src/__tests__/index.ts:36:44)
    at /scratch/standalone/8ebcee51-9b32-4b65-8c79-d2c3106b24d7/code-assist/contents/functions-typescript/src/__tests__/index.ts:80:54`

I had to add to portions of the stack trace because of this forum error :-S

Eventually, creating objects in tests to test logic on them does not seem to be straight forward and I would not consider it as stable for now…

I can’t speak to all the points, but for the verifyOntologyEditFunction() based tests I’ve written, I always re-set all the inputs by putting the stubs that are created, or setting their properties, in a beforeEach() [https://jestjs.io/docs/api#beforeeachfn-timeout] function.

Otherwise they are edited by the functions you call in the test (not always in a predictable order), and you get intermittent failures as you can’t be sure of your tests data’s state before running each test.

1 Like

I guess i will have to experiment a little more to get this to work…