Skip to content

Commit d53b3b0

Browse files
api: throw on invalid OBS initialization options
1 parent d8345ec commit d53b3b0

3 files changed

Lines changed: 81 additions & 5 deletions

File tree

js/module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,6 +2060,9 @@ export interface INodeObs {
20602060
/**
20612061
* Initializes the global OBS runtime.
20622062
* @param options - Required runtime initialization options
2063+
* @returns The OBS video initialization result code
2064+
* @throws {TypeError} If exactly one options object is not provided, or if a required option is not a string
2065+
* @throws {Error} If the IPC call fails or OSN returns an error response without an initialization result
20632066
*/
20642067
OBS_API_initAPI(options: IOBSAPIInitializationOptions): EVideoCodes;
20652068
}

obs-studio-client/source/nodeobs_api.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,28 @@
3030

3131
Napi::ThreadSafeFunction js_thread;
3232

33+
namespace {
34+
35+
bool getRequiredStringOption(const Napi::CallbackInfo &info, const Napi::Object &options, const char *name, std::string &value)
36+
{
37+
const Napi::Value option = options.Get(name);
38+
if (info.Env().IsExceptionPending())
39+
return false;
40+
41+
if (!option.IsString()) {
42+
Napi::TypeError::New(info.Env(), std::string("OBS_API_initAPI option '") + name + "' must be a string").ThrowAsJavaScriptException();
43+
return false;
44+
}
45+
46+
value = option.As<Napi::String>().Utf8Value();
47+
return true;
48+
}
49+
50+
} // namespace
51+
3352
Napi::Value api::OBS_API_initAPI(const Napi::CallbackInfo &info)
3453
{
35-
if (info.Length() != 1 || !info[0].IsObject()) {
54+
if (info.Length() != 1 || !info[0].IsObject() || info[0].IsArray()) {
3655
Napi::TypeError::New(info.Env(), "OBS_API_initAPI expects exactly one initialization options object").ThrowAsJavaScriptException();
3756
return info.Env().Undefined();
3857
}
@@ -43,10 +62,10 @@ Napi::Value api::OBS_API_initAPI(const Napi::CallbackInfo &info)
4362
std::string crashserverurl;
4463

4564
const Napi::Object options = info[0].As<Napi::Object>();
46-
ASSERT_GET_VALUE(info, options.Get("language"), language);
47-
ASSERT_GET_VALUE(info, options.Get("appDataPath"), appDataPath);
48-
ASSERT_GET_VALUE(info, options.Get("version"), version);
49-
ASSERT_GET_VALUE(info, options.Get("crashServerUrl"), crashserverurl);
65+
if (!getRequiredStringOption(info, options, "language", language) || !getRequiredStringOption(info, options, "appDataPath", appDataPath) ||
66+
!getRequiredStringOption(info, options, "version", version) || !getRequiredStringOption(info, options, "crashServerUrl", crashserverurl)) {
67+
return info.Env().Undefined();
68+
}
5069

5170
auto conn = GetConnection(info);
5271
if (!conn)

tests/osn-tests/src/test_nodeobs_api.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,60 @@ import { showHideInputHotkeys, slideshowHotkeys, ffmpeg_sourceHotkeys,
99

1010
const testName = 'nodeobs_api';
1111

12+
const validInitializationOptions: osn.IOBSAPIInitializationOptions = {
13+
language: 'en-US',
14+
appDataPath: 'test-app-data',
15+
version: '0.00.00-preview.0',
16+
crashServerUrl: '',
17+
};
18+
19+
function callInitialize(...args: unknown[]): unknown {
20+
return Reflect.apply(
21+
osn.NodeObs.OBS_API_initAPI as (...values: unknown[]) => unknown,
22+
osn.NodeObs,
23+
args,
24+
);
25+
}
26+
27+
describe(`${testName} initialization validation`, function() {
28+
it('requires exactly one non-array options object', function() {
29+
const invalidArgumentLists: unknown[][] = [
30+
[],
31+
[null],
32+
[undefined],
33+
['options'],
34+
[[]],
35+
[validInitializationOptions, validInitializationOptions],
36+
];
37+
38+
for (const args of invalidArgumentLists) {
39+
expect(() => callInitialize(...args)).to.throw(
40+
TypeError,
41+
'OBS_API_initAPI expects exactly one initialization options object',
42+
);
43+
}
44+
});
45+
46+
const requiredStringOptions: Array<keyof osn.IOBSAPIInitializationOptions> = [
47+
'language',
48+
'appDataPath',
49+
'version',
50+
'crashServerUrl',
51+
];
52+
53+
for (const optionName of requiredStringOptions) {
54+
it(`requires ${optionName} to be a string`, function() {
55+
const expectedMessage = `OBS_API_initAPI option '${optionName}' must be a string`;
56+
const missingOption: Partial<osn.IOBSAPIInitializationOptions> = { ...validInitializationOptions };
57+
delete missingOption[optionName];
58+
const invalidOption = { ...validInitializationOptions, [optionName]: 42 };
59+
60+
expect(() => callInitialize(missingOption)).to.throw(TypeError, expectedMessage);
61+
expect(() => callInitialize(invalidOption)).to.throw(TypeError, expectedMessage);
62+
});
63+
}
64+
});
65+
1266
describe(testName, function() {
1367
let obs: OBSHandler;
1468
let hasTestFailed: boolean = false;

0 commit comments

Comments
 (0)