Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .cursor/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"mcpServers": {
"nx-mcp": {
"url": "http://localhost:9267/sse"
}
}
}
4 changes: 2 additions & 2 deletions libs/csharp-ast/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export default {
coverageDirectory: "../../coverage/libs/csharp-ast",
coverageThreshold: {
global: {
branches: 73,
lines: 74,
branches: 90,
lines: 90,
},
},
};
68 changes: 68 additions & 0 deletions libs/csharp-ast/src/lib/ast/Access.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Access } from "./Access";

describe("Access", () => {
it("should define Public access modifier", () => {
expect(Access.Public).toBe("public");
});

it("should define Private access modifier", () => {
expect(Access.Private).toBe("private");
});

it("should define Protected access modifier", () => {
expect(Access.Protected).toBe("protected");
});

it("should maintain constant values", () => {
const initialValues = {
Public: Access.Public,
Private: Access.Private,
Protected: Access.Protected,
};

expect(Access.Public).toBe(initialValues.Public);
expect(Access.Private).toBe(initialValues.Private);
expect(Access.Protected).toBe(initialValues.Protected);
});

it("should not allow regular modification due to TypeScript readonly properties", () => {
// We validate this with TypeScript's type checking
// This line would error at compile time if uncommented:
// Access.Public = "modified";

// Runtime check for object as const
expect(typeof Access).toBe("object");
expect(Object.keys(Access)).toEqual(["Public", "Private", "Protected"]);
});

it("should support type checking with string literals", () => {
const publicAccess: Access = "public";
const privateAccess: Access = "private";
const protectedAccess: Access = "protected";

expect(publicAccess).toBe(Access.Public);
expect(privateAccess).toBe(Access.Private);
expect(protectedAccess).toBe(Access.Protected);
});

it("should be usable in switch statements", () => {
const getDescription = (access: Access): string => {
switch (access) {
case Access.Public:
return "visible to all";
case Access.Private:
return "visible only within class";
case Access.Protected:
return "visible within class and subclasses";
default:
return "unknown";
}
};

expect(getDescription(Access.Public)).toBe("visible to all");
expect(getDescription(Access.Private)).toBe("visible only within class");
expect(getDescription(Access.Protected)).toBe(
"visible within class and subclasses",
);
});
});
214 changes: 214 additions & 0 deletions libs/csharp-ast/src/lib/ast/Annotation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import { Annotation } from "./Annotation";
import { ClassReference } from "./ClassReference";
import { Writer } from "../core/Writer";
import { AstNode } from "../core/AstNode";

describe("Annotation", () => {
describe("constructor", () => {
it("should initialize with reference", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

const annotation = new Annotation({
reference,
});

expect(annotation["reference"]).toBe(reference);
expect(annotation["argument"]).toBeUndefined();
});

it("should initialize with string argument", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

const annotation = new Annotation({
reference,
argument: "argumentValue",
});

expect(annotation["reference"]).toBe(reference);
expect(annotation["argument"]).toBe("argumentValue");
});

it("should initialize with AstNode argument", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

// Creating a simple AstNode for testing
class TestNode extends AstNode {
public write(writer: Writer): void {
writer.write("testNode");
}
}

const argumentNode = new TestNode();

const annotation = new Annotation({
reference,
argument: argumentNode,
});

expect(annotation["reference"]).toBe(reference);
expect(annotation["argument"]).toBe(argumentNode);
});
});

describe("write", () => {
it("should write annotation without arguments", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

const annotation = new Annotation({
reference,
});

const writer = new Writer({});
annotation.write(writer);

const result = writer.toString();
expect(result).toContain("TestAttribute()");
expect(result).toContain("using Test.Namespace;");
});

it("should write annotation with string argument", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

const annotation = new Annotation({
reference,
argument: '"argumentValue"',
});

const writer = new Writer({});
annotation.write(writer);

const result = writer.toString();
expect(result).toContain('TestAttribute("argumentValue")');
expect(result).toContain("using Test.Namespace;");
});

it("should write annotation with complex string argument", () => {
const reference = new ClassReference({
name: "DisplayAttribute",
namespace: "System.ComponentModel.DataAnnotations",
});

const annotation = new Annotation({
reference,
argument: 'Name = "Username", Description = "Your login name"',
});

const writer = new Writer({});
annotation.write(writer);

const result = writer.toString();
expect(result).toContain(
'DisplayAttribute(Name = "Username", Description = "Your login name")',
);
expect(result).toContain("using System.ComponentModel.DataAnnotations;");
});

it("should write annotation with AstNode argument", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

// Creating a simple AstNode for testing
class TestNode extends AstNode {
public write(writer: Writer): void {
writer.write("testNodeValue");
}
}

const argumentNode = new TestNode();

const annotation = new Annotation({
reference,
argument: argumentNode,
});

const writer = new Writer({});
annotation.write(writer);

const result = writer.toString();
expect(result).toContain("TestAttribute(testNodeValue)");
expect(result).toContain("using Test.Namespace;");
});

it("should add reference to writer", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

const annotation = new Annotation({
reference,
});

const writer = new Writer({});

// Create a spy on addReference
const addReferenceSpy = jest.spyOn(writer, "addReference");

annotation.write(writer);

expect(addReferenceSpy).toHaveBeenCalledWith(reference);

// Clean up
addReferenceSpy.mockRestore();
});
});

describe("toString", () => {
it("should return string representation of annotation", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

const annotation = new Annotation({
reference,
argument: '"argumentValue"',
});

const result = annotation.toString();

expect(result).toContain('TestAttribute("argumentValue")');
expect(result).toContain("using Test.Namespace;");
});

it("should create writer and call write method", () => {
const reference = new ClassReference({
name: "TestAttribute",
namespace: "Test.Namespace",
});

const annotation = new Annotation({
reference,
});

// Create a spy on the write method
const writeSpy = jest.spyOn(annotation, "write");

annotation.toString();

// Verify write was called with a Writer instance
expect(writeSpy).toHaveBeenCalledTimes(1);
expect(writeSpy.mock.calls[0][0]).toBeInstanceOf(Writer);

// Clean up
writeSpy.mockRestore();
});
});
});
Loading
Loading