Create integration test
This commit is contained in:
parent
6e2754225f
commit
8763132d35
5 changed files with 87 additions and 20 deletions
17
build.zig
17
build.zig
|
@ -17,6 +17,12 @@ const sql_pkg = std.build.Pkg{
|
||||||
.dependencies = &.{util_pkg},
|
.dependencies = &.{util_pkg},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const main_pkg = std.build.Pkg{
|
||||||
|
.name = "main",
|
||||||
|
.source = std.build.FileSource.relative("src/main/main.zig"),
|
||||||
|
.dependencies = &.{ util_pkg, http_pkg, sql_pkg },
|
||||||
|
};
|
||||||
|
|
||||||
pub fn build(b: *std.build.Builder) void {
|
pub fn build(b: *std.build.Builder) void {
|
||||||
// Standard target options allows the person running `zig build` to choose
|
// Standard target options allows the person running `zig build` to choose
|
||||||
// what target to build for. Here we do not override the defaults, which
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
@ -50,6 +56,17 @@ pub fn build(b: *std.build.Builder) void {
|
||||||
unit_tests.dependOn(&http_tests.step);
|
unit_tests.dependOn(&http_tests.step);
|
||||||
unit_tests.dependOn(&sql_tests.step);
|
unit_tests.dependOn(&sql_tests.step);
|
||||||
|
|
||||||
|
const api_integration = b.addTest("./tests/api_integration/lib.zig");
|
||||||
|
api_integration.addPackage(sql_pkg);
|
||||||
|
api_integration.addPackage(util_pkg);
|
||||||
|
api_integration.addPackage(http_pkg);
|
||||||
|
api_integration.addPackage(main_pkg);
|
||||||
|
api_integration.linkLibC();
|
||||||
|
api_integration.linkSystemLibrary("sqlite3");
|
||||||
|
|
||||||
|
const integration_tests = b.step("integration-tests", "run tests");
|
||||||
|
integration_tests.dependOn(&api_integration.step);
|
||||||
|
|
||||||
const run_cmd = exe.run();
|
const run_cmd = exe.run();
|
||||||
//run_cmd.step.dependOn(b.getInstallStep());
|
//run_cmd.step.dependOn(b.getInstallStep());
|
||||||
if (b.args) |args| {
|
if (b.args) |args| {
|
||||||
|
|
|
@ -101,11 +101,10 @@ pub const ApiSource = struct {
|
||||||
pub const Conn = ApiConn(db.Database);
|
pub const Conn = ApiConn(db.Database);
|
||||||
|
|
||||||
const root_username = "root";
|
const root_username = "root";
|
||||||
const root_password_envvar = "CLUSTER_ROOT_PASSWORD";
|
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, cfg: Config) !ApiSource {
|
pub fn init(alloc: std.mem.Allocator, cfg: Config, root_password: ?[]const u8) !ApiSource {
|
||||||
var self = ApiSource{
|
var self = ApiSource{
|
||||||
.db = try db.Database.init(),
|
.db = try db.Database.init(cfg.db.sqlite.db_file),
|
||||||
.internal_alloc = alloc,
|
.internal_alloc = alloc,
|
||||||
.config = cfg,
|
.config = cfg,
|
||||||
};
|
};
|
||||||
|
@ -113,15 +112,13 @@ pub const ApiSource = struct {
|
||||||
if ((try services.users.lookupByUsername(&self.db, root_username, null)) == null) {
|
if ((try services.users.lookupByUsername(&self.db, root_username, null)) == null) {
|
||||||
std.log.info("No cluster root user detected. Creating...", .{});
|
std.log.info("No cluster root user detected. Creating...", .{});
|
||||||
|
|
||||||
const root_password = std.os.getenv(root_password_envvar) orelse {
|
// TODO: Fix this
|
||||||
std.log.err(
|
const password = root_password orelse return error.NeedRootPassword;
|
||||||
"No root user created and no password specified. Please provide the password for the root user by the ${s} environment variable for initial startup. This only needs to be done once",
|
std.debug.print("\npassword: {s}\n", .{password});
|
||||||
.{root_password_envvar},
|
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||||
);
|
defer arena.deinit();
|
||||||
@panic("No root password provided");
|
const user_id = try services.users.create(&self.db, root_username, password, null, .{}, arena.allocator());
|
||||||
};
|
std.debug.print("Created {s} ID {}", .{ root_username, user_id });
|
||||||
|
|
||||||
_ = try services.users.create(&self.db, root_username, root_password, null, .{}, alloc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|
|
@ -131,8 +131,8 @@ pub const ExecError = sql.PrepareError || sql.RowGetError || sql.BindError || st
|
||||||
pub const Database = struct {
|
pub const Database = struct {
|
||||||
db: sql.Sqlite,
|
db: sql.Sqlite,
|
||||||
|
|
||||||
pub fn init() !Database {
|
pub fn init(file_path: [:0]const u8) !Database {
|
||||||
var db = try sql.Sqlite.open("./test.db");
|
var db = try sql.Sqlite.open(file_path);
|
||||||
errdefer db.close();
|
errdefer db.close();
|
||||||
|
|
||||||
try migrations.up(&db);
|
try migrations.up(&db);
|
||||||
|
|
|
@ -3,7 +3,7 @@ const builtin = @import("builtin");
|
||||||
const http = @import("http");
|
const http = @import("http");
|
||||||
const util = @import("util");
|
const util = @import("util");
|
||||||
|
|
||||||
const api = @import("./api.zig");
|
pub const api = @import("./api.zig");
|
||||||
const models = @import("./db/models.zig");
|
const models = @import("./db/models.zig");
|
||||||
const Uuid = util.Uuid;
|
const Uuid = util.Uuid;
|
||||||
const c = @import("./controllers.zig");
|
const c = @import("./controllers.zig");
|
||||||
|
@ -45,13 +45,13 @@ fn prepare(comptime route_desc: type) Route {
|
||||||
|
|
||||||
pub const RequestServer = struct {
|
pub const RequestServer = struct {
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
api: api.ApiSource,
|
api: *api.ApiSource,
|
||||||
config: Config,
|
config: Config,
|
||||||
|
|
||||||
fn init(alloc: std.mem.Allocator, config: Config) !RequestServer {
|
fn init(alloc: std.mem.Allocator, src: *api.ApiSource, config: Config) !RequestServer {
|
||||||
return RequestServer{
|
return RequestServer{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.api = try api.ApiSource.init(alloc, config),
|
.api = src,
|
||||||
.config = config,
|
.config = config,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,12 @@ pub const RequestServer = struct {
|
||||||
|
|
||||||
pub const Config = struct {
|
pub const Config = struct {
|
||||||
cluster_host: []const u8,
|
cluster_host: []const u8,
|
||||||
|
db: struct {
|
||||||
|
sqlite: struct {
|
||||||
|
db_file: [:0]const u8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
root_password: ?[]const u8 = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn loadConfig(alloc: std.mem.Allocator) !Config {
|
fn loadConfig(alloc: std.mem.Allocator) !Config {
|
||||||
|
@ -95,10 +101,21 @@ fn loadConfig(alloc: std.mem.Allocator) !Config {
|
||||||
return std.json.parse(Config, &ts, .{ .allocator = alloc });
|
return std.json.parse(Config, &ts, .{ .allocator = alloc });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const root_password_envvar = "CLUSTER_ROOT_PASSWORD";
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
const cfg = try loadConfig(gpa.allocator());
|
var cfg = try loadConfig(gpa.allocator());
|
||||||
var srv = try RequestServer.init(gpa.allocator(), cfg);
|
var api_src = api.ApiSource.init(gpa.allocator(), cfg, std.os.getenv(root_password_envvar)) catch |err| switch (err) {
|
||||||
|
error.NeedRootPassword => {
|
||||||
|
std.log.err(
|
||||||
|
"No root user created and no password specified. Please provide the password for the root user by the ${s} environment variable for initial startup. This only needs to be done once",
|
||||||
|
.{root_password_envvar},
|
||||||
|
);
|
||||||
|
return err;
|
||||||
|
},
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
var srv = try RequestServer.init(gpa.allocator(), &api_src, cfg);
|
||||||
api.initThreadPrng(@bitCast(u64, std.time.milliTimestamp()));
|
api.initThreadPrng(@bitCast(u64, std.time.milliTimestamp()));
|
||||||
return srv.listenAndRun(std.net.Address.parseIp("0.0.0.0", 8080) catch unreachable);
|
return srv.listenAndRun(std.net.Address.parseIp("0.0.0.0", 8080) catch unreachable);
|
||||||
}
|
}
|
||||||
|
|
36
tests/api_integration/lib.zig
Normal file
36
tests/api_integration/lib.zig
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const main = @import("main");
|
||||||
|
|
||||||
|
const cluster_host = "test_host";
|
||||||
|
const test_config = .{
|
||||||
|
.cluster_host = cluster_host,
|
||||||
|
.db = .{
|
||||||
|
.sqlite = .{
|
||||||
|
.db_file = ":memory:",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ApiSource = main.api.ApiSource;
|
||||||
|
const root_password = "password";
|
||||||
|
const random_seed = 1234;
|
||||||
|
|
||||||
|
fn makeApi(alloc: std.mem.Allocator) !ApiSource {
|
||||||
|
main.api.initThreadPrng(random_seed);
|
||||||
|
|
||||||
|
const source = try ApiSource.init(alloc, test_config, root_password);
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "login as root" {
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||||
|
defer arena.deinit();
|
||||||
|
var src = try makeApi(alloc);
|
||||||
|
|
||||||
|
std.debug.print("\npassword: {s}\n", .{root_password});
|
||||||
|
var api = try src.connectUnauthorized(cluster_host, arena.allocator());
|
||||||
|
defer api.close();
|
||||||
|
|
||||||
|
_ = try api.login("root", root_password);
|
||||||
|
}
|
Loading…
Reference in a new issue