Skip to content

Commit 48cf4a3

Browse files
committed
test: fix dbt project unit tests by adding missing command factory methods and fixing NoCredentialsError test
1 parent 977ccc6 commit 48cf4a3

File tree

6 files changed

+456
-482
lines changed

6 files changed

+456
-482
lines changed

src/dbt_client/dbtCloudIntegration.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -537,11 +537,16 @@ export class DBTCloudProjectIntegration
537537
);
538538
}
539539

540-
async runModelTest(command: DBTCommand) {
541-
this.addCommandToQueue(
542-
DBTCloudProjectIntegration.QUEUE_ALL,
543-
await this.addDeferParams(this.dbtCloudCommand(command)),
544-
);
540+
async runModelTest(
541+
command: DBTCommand,
542+
): Promise<{ stdout: string; stderr: string; fullOutput: string }> {
543+
const testModelCommand = this.dbtCloudCommand(command);
544+
const result = await testModelCommand.execute();
545+
return {
546+
stdout: result.stdout,
547+
stderr: result.stderr,
548+
fullOutput: result.stdout + result.stderr,
549+
};
545550
}
546551

547552
async compileModel(command: DBTCommand) {

src/dbt_client/dbtCoreIntegration.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -665,10 +665,16 @@ export class DBTCoreProjectIntegration
665665
);
666666
}
667667

668-
async runModelTest(command: DBTCommand) {
669-
this.addCommandToQueue(
670-
await this.addDeferParams(this.dbtCoreCommand(command)),
671-
);
668+
async runModelTest(
669+
command: DBTCommand,
670+
): Promise<{ stdout: string; stderr: string; fullOutput: string }> {
671+
const testModelCommand = this.dbtCoreCommand(command);
672+
const result = await testModelCommand.execute();
673+
return {
674+
stdout: result.stdout,
675+
stderr: result.stderr,
676+
fullOutput: result.stdout + result.stderr,
677+
};
672678
}
673679

674680
async compileModel(command: DBTCommand) {

src/dbt_client/dbtIntegration.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,12 @@ export interface DBTProjectIntegration extends Disposable {
348348
): Promise<QueryExecution>;
349349
// dbt commands
350350
runModel(command: DBTCommand): Promise<void>;
351+
runModelTest(
352+
command: DBTCommand,
353+
): Promise<{ stdout: string; stderr: string; fullOutput: string }>;
351354
buildModel(command: DBTCommand): Promise<void>;
352355
buildProject(command: DBTCommand): Promise<void>;
353356
runTest(command: DBTCommand): Promise<void>;
354-
runModelTest(command: DBTCommand): Promise<void>;
355357
compileModel(command: DBTCommand): Promise<void>;
356358
generateDocs(command: DBTCommand): Promise<void>;
357359
executeCommandImmediately(command: DBTCommand): Promise<CommandProcessResult>;

src/manifest/dbtProject.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -572,10 +572,18 @@ export class DBTProject implements Disposable {
572572
try {
573573
const testModelCommand =
574574
this.dbtCommandFactory.createTestModelCommand(modelName);
575-
await this.dbtProjectIntegration.runModelTest(testModelCommand);
575+
const result =
576+
await this.dbtProjectIntegration.runModelTest(testModelCommand);
577+
const returnValue = {
578+
stdout: result.stdout,
579+
stderr: result.stderr,
580+
fullOutput: result.fullOutput,
581+
};
576582
await this.telemetry.sendTelemetryEvent("runModelTest");
583+
return returnValue;
577584
} catch (error) {
578585
this.handleNoCredentialsError(error);
586+
return { stdout: "", stderr: "", fullOutput: "" };
579587
}
580588
}
581589

@@ -595,9 +603,6 @@ export class DBTProject implements Disposable {
595603
}
596604

597605
async generateDocsImmediately(args?: string[]) {
598-
if (!this.altimate.handlePreviewFeatures()) {
599-
return;
600-
}
601606
try {
602607
const docsGenerateCommand =
603608
this.dbtCommandFactory.createDocsGenerateCommand();
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import * as assert from "assert";
2+
import * as sinon from "sinon";
3+
import { DBTCoreProjectIntegration } from "../../dbt_client/dbtCoreIntegration";
4+
import { Container } from "inversify";
5+
import { DBTTerminal } from "../../dbt_client/dbtTerminal";
6+
import {
7+
DBTCommandExecutionInfrastructure,
8+
PythonDBTCommandExecutionStrategy,
9+
} from "../../dbt_client/dbtIntegration";
10+
import { PythonBridge } from "python-bridge";
11+
import { PythonEnvironment } from "../../manifest/pythonEnvironment";
12+
import { TelemetryService } from "../../telemetry";
13+
import { DBTProjectContainer } from "../../manifest/dbtProjectContainer";
14+
import { AltimateRequest } from "../../altimate";
15+
import { ValidationProvider } from "../../validation_provider";
16+
import { DeferToProdService } from "../../services/deferToProdService";
17+
import { Uri, languages, EventEmitter } from "vscode";
18+
19+
suite("DBTCoreProjectIntegration Tests", () => {
20+
let sandbox: sinon.SinonSandbox;
21+
let dbtCoreProjectIntegration: DBTCoreProjectIntegration;
22+
let mockPythonBridge: any;
23+
24+
setup(() => {
25+
sandbox = sinon.createSandbox();
26+
// Create mock dependencies
27+
const container = new Container();
28+
29+
// Mock all required dependencies
30+
mockPythonBridge = {
31+
lock: sandbox.stub(),
32+
};
33+
34+
const mockExecutionInfrastructure = {
35+
createPythonBridge: () => mockPythonBridge,
36+
createQueue: () => {},
37+
};
38+
39+
const mockUri = Uri.file("/test/project/root");
40+
const mockDiagnosticCollection =
41+
languages.createDiagnosticCollection("dbt-project-config");
42+
43+
// Create mock event emitter for Python environment changes
44+
const mockPythonEnvChangeEmitter = new EventEmitter<Uri | undefined>();
45+
46+
// Create the instance directly with constructor injection
47+
dbtCoreProjectIntegration = new DBTCoreProjectIntegration(
48+
mockExecutionInfrastructure as any,
49+
{
50+
onPythonEnvironmentChanged: mockPythonEnvChangeEmitter.event,
51+
pythonPath: "/usr/bin/python3",
52+
environmentVariables: {},
53+
} as any,
54+
{} as TelemetryService,
55+
{} as PythonDBTCommandExecutionStrategy,
56+
{} as DBTProjectContainer,
57+
{} as AltimateRequest,
58+
{
59+
debug: () => {},
60+
error: () => {},
61+
log: () => {},
62+
} as any,
63+
{} as ValidationProvider,
64+
{} as DeferToProdService,
65+
mockUri,
66+
mockDiagnosticCollection,
67+
);
68+
69+
(dbtCoreProjectIntegration as any).python = mockPythonBridge;
70+
});
71+
72+
teardown(() => {
73+
sandbox.restore();
74+
});
75+
76+
test("validateSql should return validation result for valid SQL", async () => {
77+
// Arrange
78+
const query = "SELECT * FROM my_table";
79+
const dialect = "postgres";
80+
const models = {};
81+
const expectedResult = {
82+
valid: true,
83+
errors: [],
84+
};
85+
86+
mockPythonBridge.lock.resolves(expectedResult);
87+
88+
// Act
89+
const result = await dbtCoreProjectIntegration.validateSql(
90+
query,
91+
dialect,
92+
models,
93+
);
94+
95+
// Assert
96+
assert.deepStrictEqual(result, expectedResult);
97+
assert.ok(mockPythonBridge.lock.calledOnce);
98+
const lockCall = mockPythonBridge.lock.getCall(0);
99+
assert.ok(lockCall.args[0].toString().includes("validate_sql"));
100+
});
101+
102+
test("validateSql should handle invalid SQL", async () => {
103+
const query = "SELECT * FREM my_table"; // Intentional typo
104+
const dialect = "postgres";
105+
const models = {};
106+
const expectedResult = {
107+
valid: false,
108+
errors: [
109+
{
110+
message: 'syntax error at or near "FREM"',
111+
line: 1,
112+
column: 8,
113+
},
114+
],
115+
};
116+
117+
mockPythonBridge.lock.resolves(expectedResult);
118+
119+
// Act
120+
const result = await dbtCoreProjectIntegration.validateSql(
121+
query,
122+
dialect,
123+
models,
124+
);
125+
126+
// Assert
127+
assert.deepStrictEqual(result, expectedResult);
128+
assert.ok(mockPythonBridge.lock.calledOnce);
129+
});
130+
});

0 commit comments

Comments
 (0)