Skip to content
Open
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
23 changes: 23 additions & 0 deletions docs/modules/oraclefree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Oracle Free

## Install

```bash
npm install @testcontainers/oraclefree --save-dev
```

## Examples

These examples use the following libraries:

- [oracledb](https://www.npmjs.com/package/oracledb)

npm install oracledb

Recommended to use an image from [this registry](https://hub.docker.com/r/gvenzl/oracle-free) and substitute for `IMAGE`

### Start a database and execute queries

<!--codeinclude-->
[](../../packages/modules/oraclefree/src/oraclefree-container.test.ts) inside_block:customDatabase
<!--/codeinclude-->
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ nav:
- Neo4J: modules/neo4j.md
- Ollama: modules/ollama.md
- OpenSearch: modules/opensearch.md
- Oracle Free: modules/oraclefree.md
- PostgreSQL: modules/postgresql.md
- Qdrant: modules/qdrant.md
- RabbitMQ: modules/rabbitmq.md
Expand Down
37 changes: 37 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/modules/oraclefree/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FROM gvenzl/oracle-free:slim-faststart
Copy link
Collaborator

@cristianrgreco cristianrgreco Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's pin the image version, gvenzl/oracle-free:23.26.1-slim-faststart is equivalent to current latest. Dependabot will keep it up to date and will catch future breaking changes

39 changes: 39 additions & 0 deletions packages/modules/oraclefree/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@testcontainers/oraclefree",
"version": "11.12.0",
"license": "MIT",
"keywords": [
"oracle",
"database",
"testing",
"docker",
"testcontainers"
],
"description": "Oracle DB Free module for Testcontainers",
"homepage": "https://github.com/testcontainers/testcontainers-node#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/testcontainers/testcontainers-node.git"
},
"bugs": {
"url": "https://github.com/testcontainers/testcontainers-node/issues"
},
"main": "build/index.js",
"files": [
"build"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"prepack": "shx cp ../../../README.md . && shx cp ../../../LICENSE .",
"build": "tsc --project tsconfig.build.json"
},
"dependencies": {
"testcontainers": "^11.12.0"
},
"devDependencies": {
"oracledb": "^6.10.0",
"@types/oracledb": "^6.10.0"
}
}
1 change: 1 addition & 0 deletions packages/modules/oraclefree/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { OracleDbContainer, StartedOracleDbContainer } from "./oraclefree-container";
107 changes: 107 additions & 0 deletions packages/modules/oraclefree/src/oraclefree-container.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import oracledb from "oracledb";
import { getImage } from "../../../testcontainers/src/utils/test-helper";
import { OracleDbContainer, StartedOracleDbContainer } from "./oraclefree-container";

const IMAGE = getImage(__dirname);

describe.sequential("OracleFreeContainer", { timeout: 240_000 }, () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can run in parallel

describe("default configuration", () => {
let container: StartedOracleDbContainer;

beforeAll(async () => {
container = await new OracleDbContainer(IMAGE).start();
}, 120_000);

afterAll(async () => {
await container.stop();
});

it("should connect and return a query result", async () => {
const connection = await oracledb.getConnection({
user: container.getUsername(),
password: container.getPassword(),
connectString: container.getUrl(),
});

const result = await connection.execute("SELECT 1 FROM DUAL");
expect(result.rows![0]).toEqual([1]);

await connection.close();
});

it("should work with connection descriptor", async () => {
const connection = await oracledb.getConnection({
user: container.getUsername(),
password: container.getPassword(),
connectString: container.getConnectionDescriptor(),
});

const result = await connection.execute("SELECT 1 FROM DUAL");
expect(result.rows![0]).toEqual([1]);

await connection.close();
});

it("should work with restarted container", async () => {
await container.restart();

const connection = await oracledb.getConnection({
user: container.getUsername(),
password: container.getPassword(),
connectString: container.getUrl(),
});

const result = await connection.execute("SELECT 1 FROM DUAL");
expect(result.rows![0]).toEqual([1]);

await connection.close();
});

it("should have default database name", async () => {
const connection = await oracledb.getConnection({
user: container.getUsername(),
password: container.getPassword(),
connectString: container.getUrl(),
});

const result = await connection.execute("SELECT SYS_CONTEXT('USERENV', 'CON_NAME') FROM DUAL");
expect(result.rows![0]).toEqual(["FREEPDB1"]);

await connection.close();
});
});

it("should treat default database names as no-op and reject empty names", () => {
const container = new OracleDbContainer(IMAGE);
expect(() => container.withDatabase("FREEPDB1")).not.toThrow();
expect(() => container.withDatabase("freepdb1")).not.toThrow();
expect(() => container.withDatabase("")).toThrow("Database name cannot be empty.");
});

it("should set the custom database and user", async () => {
// customDatabase {
const customDatabase = "TESTDB";
const customUsername = "CUSTOMUSER";
const customPassword = "customPassword";
await using container = await new OracleDbContainer(IMAGE)
.withDatabase(customDatabase)
.withUsername(customUsername)
.withPassword(customPassword)
.start();

const connection = await oracledb.getConnection({
user: container.getUsername(),
password: container.getPassword(),
connectString: container.getUrl(),
});

const result = await connection.execute("SELECT SYS_CONTEXT('USERENV', 'CON_NAME') FROM DUAL");
expect(result.rows![0]).toEqual([customDatabase]);

const resultUser = await connection.execute("SELECT USER FROM DUAL");
expect(resultUser.rows![0]).toEqual([customUsername]);

await connection.close();
// }
});
});
115 changes: 115 additions & 0 deletions packages/modules/oraclefree/src/oraclefree-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait } from "testcontainers";

/** Default Oracle listener port. */
const ORACLEDB_PORT = 1521;
/** Default pluggable database name provided by the Oracle Free image. */
const DEFAULT_DATABASE = "FREEPDB1";

/**
* Testcontainers wrapper for Oracle Free.
*
* Supports configuring application user credentials and optional custom database creation.
*/
export class OracleDbContainer extends GenericContainer {
private username = "test";
private password = "test";
private database?: string = undefined;

constructor(image: string) {
super(image);
this.withExposedPorts(ORACLEDB_PORT);
this.withWaitStrategy(Wait.forLogMessage("DATABASE IS READY TO USE!"));
this.withStartupTimeout(120_000);
}

/** Sets the application username created at container startup. */
public withUsername(username: string): this {
this.username = username;
return this;
}

/** Sets the password for both SYS and application user startup configuration. */
public withPassword(password: string): this {
this.password = password;
return this;
}

/** Sets a custom application database/service name. */
public withDatabase(database: string): this {
if (database.trim() === "") {
throw new Error("Database name cannot be empty.");
}

if (database.toUpperCase() === DEFAULT_DATABASE) {
this.database = undefined;
return this;
}

this.database = database;
return this;
}

/** Starts the container and returns a typed started container instance. */
public override async start(): Promise<StartedOracleDbContainer> {
this.withEnvironment({
ORACLE_PASSWORD: this.password,
APP_USER: this.username,
APP_USER_PASSWORD: this.password,
});

if (this.database) {
this.withEnvironment({
ORACLE_DATABASE: this.database,
});
}

return new StartedOracleDbContainer(
await super.start(),
this.username,
this.password,
this.database ?? DEFAULT_DATABASE
);
}
}

/** Represents a running Oracle Free test container with Oracle-specific accessors. */
export class StartedOracleDbContainer extends AbstractStartedContainer {
constructor(
startedTestContainer: StartedTestContainer,
private readonly username: string,
private readonly password: string,
private readonly database: string
) {
super(startedTestContainer);
}

/** Returns the mapped Oracle listener port. */
public getPort(): number {
return this.getMappedPort(ORACLEDB_PORT);
}

/** Returns the configured application username. */
public getUsername(): string {
return this.username;
}

/** Returns the configured password. */
public getPassword(): string {
return this.password;
}

/** Returns the configured service/database name. */
public getDatabase(): string {
return this.database;
}

/** Returns a host:port/database URL fragment. */
public getUrl(): string {
return `${this.getHost()}:${this.getPort()}/${this.database}`;
}

/** Returns an Oracle connection descriptor string (TNS format). */
public getConnectionDescriptor(): string {
return `(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=${this.getHost()})(PORT=${this.getPort()}))(CONNECT_DATA=(SERVICE_NAME=${this.database})))`;
}
}
12 changes: 12 additions & 0 deletions packages/modules/oraclefree/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"exclude": [
"build",
"src/**/*.test.ts"
],
"references": [
{
"path": "../../testcontainers"
}
]
}
Loading
Loading