Skip to content

Commit

Permalink
Fix and add test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-fleck-at committed Mar 7, 2024
1 parent b2fa0bc commit 87d0e88
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 28 deletions.
Expand Up @@ -3,20 +3,32 @@
********************************************************************************/

import { describe, expect, test } from '@jest/globals';
import { EmptyFileSystem, isReference } from 'langium';
import { isReference } from 'langium';

import { relationship1, relationship2 } from './test-utils/test-documents/relationship/index.js';
import { parseDocument } from './test-utils/utils.js';
import {
relationship1,
relationship2,
relationship_with_attribute,
relationship_with_attribute_wrong_entity,
relationship_with_duplicate_attributes
} from './test-utils/test-documents/relationship/index.js';
import { createCrossModelTestServices, parseAndForgetDocument, parseDocuments } from './test-utils/utils.js';

import { createCrossModelServices } from '../../src/language-server/cross-model-module.js';
import { CrossModelRoot } from '../../src/language-server/generated/ast.js';
import { address } from './test-utils/test-documents/entity/address.js';
import { customer } from './test-utils/test-documents/entity/customer.js';
import { order } from './test-utils/test-documents/entity/order.js';

const services = createCrossModelServices({ ...EmptyFileSystem }).CrossModel;
const services = createCrossModelTestServices();

describe('CrossModel language Relationship', () => {
beforeAll(() => {
parseDocuments(services, [order, customer, address]);
});

test('Simple file for relationship', async () => {
const document = relationship1;
const parsedDocument = await parseDocument(services, document);
const parsedDocument = await parseAndForgetDocument(services, document);
const model = parsedDocument.parseResult.value as CrossModelRoot;

expect(model).toHaveProperty('relationship');
Expand All @@ -36,11 +48,47 @@ describe('CrossModel language Relationship', () => {

test('relationship with indentation error', async () => {
const document = relationship2;
const parsedDocument = await parseDocument(services, document);
const parsedDocument = await parseAndForgetDocument(services, document);
const model = parsedDocument.parseResult.value as CrossModelRoot;

expect(model).toHaveProperty('relationship');
expect(parsedDocument.parseResult.lexerErrors.length).toBe(0);
expect(parsedDocument.parseResult.parserErrors.length).toBe(1);
});

test('relationship with attributes', async () => {
const parsedDocument = await parseAndForgetDocument(services, relationship_with_attribute, {
validation: true
});
const model = parsedDocument.parseResult.value as CrossModelRoot;

expect(model).toHaveProperty('relationship');
expect(parsedDocument.parseResult.lexerErrors.length).toBe(0);
expect(parsedDocument.parseResult.parserErrors.length).toBe(0);
expect(parsedDocument.diagnostics).toHaveLength(0);
});

test('relationship with wrong entity', async () => {
const parsedDocument = await parseAndForgetDocument(services, relationship_with_attribute_wrong_entity, {
validation: true
});
const model = parsedDocument.parseResult.value as CrossModelRoot;

expect(model).toHaveProperty('relationship');
expect(parsedDocument.parseResult.lexerErrors.length).toBe(0);
expect(parsedDocument.parseResult.parserErrors.length).toBe(0);
expect(parsedDocument.diagnostics).toHaveLength(1);
});

test('relationship with duplicates', async () => {
const parsedDocument = await parseAndForgetDocument(services, relationship_with_duplicate_attributes, {
validation: true
});
const model = parsedDocument.parseResult.value as CrossModelRoot;

expect(model).toHaveProperty('relationship');
expect(parsedDocument.parseResult.lexerErrors.length).toBe(0);
expect(parsedDocument.parseResult.parserErrors.length).toBe(0);
expect(parsedDocument.diagnostics).toHaveLength(2);
});
});
Expand Up @@ -116,7 +116,8 @@ describe('CrossModelLexer', () => {
name: 'test Name',
parent: ref1,
child: ref2,
type: 'n:m'
type: 'n:m',
attributes: []
};
});

Expand Down Expand Up @@ -168,7 +169,8 @@ describe('CrossModelLexer', () => {
name: 'test Name',
parent: ref1,
child: ref2,
type: 'n:m'
type: 'n:m',
attributes: []
}
};

Expand Down
@@ -0,0 +1,16 @@
/********************************************************************************
* Copyright (c) 2024 CrossBreeze.
********************************************************************************/
export const address = `entity:
id: Address
name: "Address"
attributes:
- id: CustomerID
name: "CustomerID"
datatype: "Integer"
- id: Street
name: "Street"
datatype: "Varchar"
- id: CountryCode
name: "CountryCode"
datatype: "Varchar"`;
@@ -0,0 +1,28 @@
/********************************************************************************
* Copyright (c) 2024 CrossBreeze.
********************************************************************************/
export const customer = `entity:
id: Customer
name: "Customer"
attributes:
- id: Id
name: "Id"
datatype: "Integer"
- id: FirstName
name: "FirstName"
datatype: "Varchar"
- id: LastName
name: "LastName"
datatype: "Varchar"
- id: City
name: "City"
datatype: "Varchar"
- id: Country
name: "Country"
datatype: "Varchar"
- id: Phone
name: "Phone"
datatype: "Varchar"
- id: BirthDate
name: "BirthDate"
datatype: "DateTime"`;
@@ -1,7 +1,10 @@
/********************************************************************************
* Copyright (c) 2023 CrossBreeze.
********************************************************************************/
export * from './address.js';
export * from './customer.js';
export * from './entity1.js';
export * from './entity2.js';
export * from './entity3.js';
export * from './entity4.js';
export * from './order.js';
@@ -0,0 +1,23 @@
/********************************************************************************
* Copyright (c) 2024 CrossBreeze.
********************************************************************************/
export const order = `entity:
id: Order
name: "Order"
description: "Order placed by a customer in the Customer table."
attributes:
- id: Id
name: "Id"
datatype: "Integer"
- id: OrderDate
name: "OrderDate"
datatype: "Integer"
- id: OrderNumber
name: "OrderNumber"
datatype: "Varchar"
- id: CustomerId
name: "CustomerId"
datatype: "Integer"
- id: TotalAmount
name: "TotalAmount"
datatype: "Float"`;
Expand Up @@ -3,3 +3,4 @@
********************************************************************************/
export * from './relationship1.js';
export * from './relationship2.js';
export * from './relationship_attribute.js';
@@ -0,0 +1,35 @@
/********************************************************************************
* Copyright (c) 2024 CrossBreeze.
********************************************************************************/

/** Valid relationship with attribute */
export const relationship_with_attribute = `relationship:
id: Order_Customer
parent: Customer
child: Order
type: "1:1"
attributes:
- parent: Customer.Id
child: Order.CustomerId`;

/** Relationship with invalid attribute (wrong entity) */
export const relationship_with_attribute_wrong_entity = `relationship:
id: Order_Customer
parent: Customer
child: Order
type: "1:1"
attributes:
- parent: Customer.Id
child: Order.Address`;

/** Relationship with invalid attribute (duplicates) */
export const relationship_with_duplicate_attributes = `relationship:
id: Order_Customer
parent: Customer
child: Order
type: "1:1"
attributes:
- parent: Customer.Id
child: Order.CustomerId
- parent: Customer.Id
child: Order.CustomerId`;
45 changes: 26 additions & 19 deletions extensions/crossmodel-lang/test/language-server/test-utils/utils.ts
@@ -1,26 +1,33 @@
/********************************************************************************
* Copyright (c) 2023 CrossBreeze.
********************************************************************************/
import { AstNode, LangiumDocument, LangiumServices } from 'langium';
import { URI } from 'vscode-uri';

export async function parseDocument<T extends AstNode = AstNode>(services: LangiumServices, input: string): Promise<LangiumDocument<T>> {
const document = await parseHelper<T>(services)(input);
if (!document.parseResult) {
throw new Error('Could not parse document');
}
return document;
import { AstNode, DefaultLangiumDocuments, EmptyFileSystem, LangiumDocument, LangiumServices } from 'langium';
import { ParseHelperOptions, clearDocuments, parseDocument } from 'langium/test';
import { CrossModelServices, createCrossModelServices } from '../../../src/language-server/cross-model-module.js';

export { parseDocument } from 'langium/test';

export function createCrossModelTestServices(): CrossModelServices {
const services = createCrossModelServices({ ...EmptyFileSystem }).CrossModel;
services.shared.workspace.LangiumDocuments = new DefaultLangiumDocuments(services.shared);
return services;
}

export function parseHelper<T extends AstNode = AstNode>(services: LangiumServices): (input: string) => Promise<LangiumDocument<T>> {
const metaData = services.LanguageMetaData;
const documentBuilder = services.shared.workspace.DocumentBuilder;
return async input => {
const randomNumber = Math.floor(Math.random() * 10000000) + 1000000;
const uri = URI.parse(`file:///${randomNumber}${metaData.fileExtensions[0]}`);
const document = services.shared.workspace.LangiumDocumentFactory.fromString<T>(input, uri);
services.shared.workspace.LangiumDocuments.addDocument(document);
await documentBuilder.build([document]);
return document;
};
export async function parseDocuments<T extends AstNode = AstNode>(
services: LangiumServices,
inputs: string[],
options?: ParseHelperOptions
): Promise<LangiumDocument<T>[]> {
return Promise.all(inputs.map(input => parseDocument<T>(services, input, options)));
}

export async function parseAndForgetDocument<T extends AstNode = AstNode>(
services: LangiumServices,
input: string,
options?: ParseHelperOptions
): Promise<LangiumDocument<T>> {
const document = await parseDocument<T>(services, input, options);
await clearDocuments(services, [document]);
return document;
}

0 comments on commit 87d0e88

Please sign in to comment.