meadow 2.0.42 → 2.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meadow",
3
- "version": "2.0.42",
3
+ "version": "2.0.43",
4
4
  "description": "A data access library.",
5
5
  "main": "source/Meadow.js",
6
6
  "scripts": {
@@ -28,6 +28,11 @@
28
28
  "docker-postgresql-status": "./scripts/postgresql-test-db.sh status",
29
29
  "test-postgresql": "./scripts/postgresql-test-db.sh start && npx quack test -g Meadow-Provider-PostgreSQL",
30
30
  "test-postgresql-all": "./scripts/postgresql-test-db.sh start && npx mocha -u tdd --exit -R spec",
31
+ "docker-oracle-start": "./scripts/oracle-test-db.sh start",
32
+ "docker-oracle-stop": "./scripts/oracle-test-db.sh stop",
33
+ "docker-oracle-status": "./scripts/oracle-test-db.sh status",
34
+ "test-oracle": "./scripts/oracle-test-db.sh start && npx quack test -g Meadow-Provider-Oracle",
35
+ "test-oracle-all": "./scripts/oracle-test-db.sh start && npx mocha -u tdd --exit -R spec",
31
36
  "docker-mongodb-start": "./scripts/mongodb-test-db.sh start",
32
37
  "docker-mongodb-stop": "./scripts/mongodb-test-db.sh stop",
33
38
  "docker-mongodb-status": "./scripts/mongodb-test-db.sh status",
@@ -43,7 +48,7 @@
43
48
  "test-alasql": "npx quack test -g Meadow-Provider-ALASQL",
44
49
  "test-sqlite-browser": "npx quack test -g Meadow-Provider-SQLiteBrowser",
45
50
  "test-sqlite-browser-headless": "npx mocha -u tdd --exit --timeout 60000 test/Meadow-Provider-SQLiteBrowser-Headless_tests.js",
46
- "test-all-providers": "./scripts/mysql-test-db.sh start && ./scripts/mssql-test-db.sh start && ./scripts/postgresql-test-db.sh start && ./scripts/mongodb-test-db.sh start && ./scripts/solr-test-db.sh start && ./scripts/dgraph-test-db.sh start && npx mocha -u tdd --exit -R spec",
51
+ "test-all-providers": "./scripts/mysql-test-db.sh start && ./scripts/mssql-test-db.sh start && ./scripts/postgresql-test-db.sh start && ./scripts/oracle-test-db.sh start && ./scripts/mongodb-test-db.sh start && ./scripts/solr-test-db.sh start && ./scripts/dgraph-test-db.sh start && npx mocha -u tdd --exit -R spec",
47
52
  "docker-cleanup": "./scripts/meadow-test-cleanup.sh",
48
53
  "test-cleanup": "./scripts/meadow-test-cleanup.sh"
49
54
  },
@@ -88,13 +93,15 @@
88
93
  "meadow-connection-mongodb": "^1.0.3",
89
94
  "meadow-connection-mssql": "^1.0.23",
90
95
  "meadow-connection-mysql": "^1.0.19",
91
- "meadow-connection-postgresql": "^1.0.6",
96
+ "meadow-connection-oracle": "^1.0.2",
97
+ "meadow-connection-postgresql": "^1.0.7",
92
98
  "meadow-connection-rocksdb": "^1.0.0",
93
99
  "meadow-connection-solr": "^1.0.3",
94
100
  "meadow-connection-sqlite": "^1.0.20",
95
101
  "meadow-connection-sqlite-browser": "^1.0.2",
96
102
  "mongodb": "^6.12.0",
97
103
  "mysql2": "^3.18.2",
104
+ "oracledb": "^6.10.0",
98
105
  "pict-docuserve": "^1.4.12",
99
106
  "puppeteer": "^24.38.0",
100
107
  "quackage": "^1.3.0",
@@ -102,7 +109,7 @@
102
109
  },
103
110
  "dependencies": {
104
111
  "async": "3.2.6",
105
- "foxhound": "^2.0.28",
112
+ "foxhound": "^2.0.29",
106
113
  "is-my-json-valid": "2.20.6",
107
114
  "simple-get": "^4.0.1"
108
115
  }
@@ -496,6 +496,138 @@ function emitMSSQL()
496
496
  return tmpOut.join('\n');
497
497
  }
498
498
 
499
+ // ---------------------------------------------------------------------------
500
+ // Oracle emitter. Oracle has no CREATE TABLE IF NOT EXISTS; the docker script
501
+ // runs this seed once against a fresh container, so plain CREATE TABLE is fine.
502
+ // Output is sqlplus-friendly: SET DEFINE OFF stops the '&' in "Angels & Demons"
503
+ // from being read as a substitution variable, and WHENEVER SQLERROR EXIT
504
+ // FAILURE makes a bad statement fail the piped load. Identity columns + the
505
+ // 23ai multi-row VALUES clause keep it close to the other dialects.
506
+ // ---------------------------------------------------------------------------
507
+ function emitOracle()
508
+ {
509
+ const tmpOut = [];
510
+ tmpOut.push('-- Bookstore Schema and Seed Data for Meadow Oracle Tests');
511
+ tmpOut.push('-- Generated by scripts/bookstore-seed.js (GUIDs minted via fable-uuid)');
512
+ tmpOut.push('SET DEFINE OFF');
513
+ tmpOut.push('WHENEVER SQLERROR EXIT FAILURE');
514
+ tmpOut.push('');
515
+ tmpOut.push('CREATE TABLE Book');
516
+ tmpOut.push(' (');
517
+ tmpOut.push(' IDBook NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,');
518
+ tmpOut.push(' GUIDBook VARCHAR2(36) DEFAULT \'00000000-0000-0000-0000-000000000000\' NOT NULL,');
519
+ tmpOut.push(' CreateDate TIMESTAMP,');
520
+ tmpOut.push(' CreatingIDUser NUMBER DEFAULT 0 NOT NULL,');
521
+ tmpOut.push(' UpdateDate TIMESTAMP,');
522
+ tmpOut.push(' UpdatingIDUser NUMBER DEFAULT 0 NOT NULL,');
523
+ tmpOut.push(' Deleted NUMBER(1) DEFAULT 0 NOT NULL,');
524
+ tmpOut.push(' DeleteDate TIMESTAMP,');
525
+ tmpOut.push(' DeletingIDUser NUMBER DEFAULT 0 NOT NULL,');
526
+ tmpOut.push(' Title VARCHAR2(200) DEFAULT \' \' NOT NULL,');
527
+ tmpOut.push(' Type VARCHAR2(32) DEFAULT \' \' NOT NULL,');
528
+ tmpOut.push(' Genre VARCHAR2(128) DEFAULT \' \' NOT NULL,');
529
+ tmpOut.push(' ISBN VARCHAR2(64) DEFAULT \' \' NOT NULL,');
530
+ tmpOut.push(' Language VARCHAR2(12) DEFAULT \' \' NOT NULL,');
531
+ tmpOut.push(' ImageURL VARCHAR2(254) DEFAULT \' \' NOT NULL,');
532
+ tmpOut.push(' PublicationYear NUMBER DEFAULT 0 NOT NULL');
533
+ tmpOut.push(' );');
534
+ tmpOut.push('');
535
+ tmpOut.push('CREATE TABLE BookAuthorJoin');
536
+ tmpOut.push(' (');
537
+ tmpOut.push(' IDBookAuthorJoin NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,');
538
+ tmpOut.push(' GUIDBookAuthorJoin VARCHAR2(36) DEFAULT \'00000000-0000-0000-0000-000000000000\' NOT NULL,');
539
+ tmpOut.push(' IDBook NUMBER DEFAULT 0 NOT NULL,');
540
+ tmpOut.push(' IDAuthor NUMBER DEFAULT 0 NOT NULL');
541
+ tmpOut.push(' );');
542
+ tmpOut.push('');
543
+ tmpOut.push('CREATE TABLE Author');
544
+ tmpOut.push(' (');
545
+ tmpOut.push(' IDAuthor NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,');
546
+ tmpOut.push(' GUIDAuthor VARCHAR2(36) DEFAULT \'00000000-0000-0000-0000-000000000000\' NOT NULL,');
547
+ tmpOut.push(' CreateDate TIMESTAMP,');
548
+ tmpOut.push(' CreatingIDUser NUMBER DEFAULT 0 NOT NULL,');
549
+ tmpOut.push(' UpdateDate TIMESTAMP,');
550
+ tmpOut.push(' UpdatingIDUser NUMBER DEFAULT 0 NOT NULL,');
551
+ tmpOut.push(' Deleted NUMBER(1) DEFAULT 0 NOT NULL,');
552
+ tmpOut.push(' DeleteDate TIMESTAMP,');
553
+ tmpOut.push(' DeletingIDUser NUMBER DEFAULT 0 NOT NULL,');
554
+ tmpOut.push(' Name VARCHAR2(200) DEFAULT \' \' NOT NULL');
555
+ tmpOut.push(' );');
556
+ tmpOut.push('');
557
+ tmpOut.push('CREATE TABLE BookPrice');
558
+ tmpOut.push(' (');
559
+ tmpOut.push(' IDBookPrice NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,');
560
+ tmpOut.push(' GUIDBookPrice VARCHAR2(36) DEFAULT \'00000000-0000-0000-0000-000000000000\' NOT NULL,');
561
+ tmpOut.push(' CreateDate TIMESTAMP,');
562
+ tmpOut.push(' CreatingIDUser NUMBER DEFAULT 0 NOT NULL,');
563
+ tmpOut.push(' UpdateDate TIMESTAMP,');
564
+ tmpOut.push(' UpdatingIDUser NUMBER DEFAULT 0 NOT NULL,');
565
+ tmpOut.push(' Deleted NUMBER(1) DEFAULT 0 NOT NULL,');
566
+ tmpOut.push(' DeleteDate TIMESTAMP,');
567
+ tmpOut.push(' DeletingIDUser NUMBER DEFAULT 0 NOT NULL,');
568
+ tmpOut.push(' Price NUMBER(8,2),');
569
+ tmpOut.push(' StartDate TIMESTAMP,');
570
+ tmpOut.push(' EndDate TIMESTAMP,');
571
+ tmpOut.push(' Discountable NUMBER(1) DEFAULT 0 NOT NULL,');
572
+ tmpOut.push(' CouponCode VARCHAR2(16) DEFAULT \' \' NOT NULL,');
573
+ tmpOut.push(' IDBook NUMBER DEFAULT 0 NOT NULL');
574
+ tmpOut.push(' );');
575
+ tmpOut.push('');
576
+ tmpOut.push('CREATE TABLE Review');
577
+ tmpOut.push(' (');
578
+ tmpOut.push(' IDReviews NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,');
579
+ tmpOut.push(' GUIDReviews VARCHAR2(36) DEFAULT \'00000000-0000-0000-0000-000000000000\' NOT NULL,');
580
+ tmpOut.push(' CreateDate TIMESTAMP,');
581
+ tmpOut.push(' CreatingIDUser NUMBER DEFAULT 0 NOT NULL,');
582
+ tmpOut.push(' UpdateDate TIMESTAMP,');
583
+ tmpOut.push(' UpdatingIDUser NUMBER DEFAULT 0 NOT NULL,');
584
+ tmpOut.push(' Deleted NUMBER(1) DEFAULT 0 NOT NULL,');
585
+ tmpOut.push(' DeleteDate TIMESTAMP,');
586
+ tmpOut.push(' DeletingIDUser NUMBER DEFAULT 0 NOT NULL,');
587
+ tmpOut.push(' Text CLOB,');
588
+ tmpOut.push(' Rating NUMBER DEFAULT 0 NOT NULL,');
589
+ tmpOut.push(' IDBook NUMBER DEFAULT 0 NOT NULL');
590
+ tmpOut.push(' );');
591
+ tmpOut.push('');
592
+ tmpOut.push('-- Seed Data: First 20 books from books.csv');
593
+ tmpOut.push('-- Inserted in CSV order so IDBook matches CSV row number');
594
+ tmpOut.push('');
595
+ tmpOut.push('INSERT INTO Book (GUIDBook, Title, Type, Genre, ISBN, Language, ImageURL, PublicationYear, CreateDate, CreatingIDUser, UpdateDate, UpdatingIDUser) VALUES');
596
+ const tmpBookRows = BOOKS.map((pBook) =>
597
+ {
598
+ return ' (' + [
599
+ sqlQuote(tmpUUID.getUUID()),
600
+ sqlQuote(pBook.Title),
601
+ sqlQuote(pBook.Type),
602
+ sqlQuote(pBook.Genre),
603
+ sqlQuote(pBook.ISBN),
604
+ sqlQuote(pBook.Language),
605
+ sqlQuote(pBook.ImageURL),
606
+ pBook.PublicationYear,
607
+ 'SYS_EXTRACT_UTC(SYSTIMESTAMP)', '99999', 'SYS_EXTRACT_UTC(SYSTIMESTAMP)', '99999'
608
+ ].join(', ') + ')';
609
+ });
610
+ tmpOut.push(tmpBookRows.join(',\n') + ';');
611
+ tmpOut.push('');
612
+ tmpOut.push('-- Seed Data: Authors for the first 20 books');
613
+ tmpOut.push('');
614
+ tmpOut.push('INSERT INTO Author (GUIDAuthor, Name, CreateDate, CreatingIDUser, UpdateDate, UpdatingIDUser) VALUES');
615
+ const tmpAuthorRows = AUTHORS.map((pName) =>
616
+ {
617
+ return ' (' + [
618
+ sqlQuote(tmpUUID.getUUID()),
619
+ sqlQuote(pName),
620
+ 'SYS_EXTRACT_UTC(SYSTIMESTAMP)', '99999', 'SYS_EXTRACT_UTC(SYSTIMESTAMP)', '99999'
621
+ ].join(', ') + ')';
622
+ });
623
+ tmpOut.push(tmpAuthorRows.join(',\n') + ';');
624
+ tmpOut.push('');
625
+ tmpOut.push('COMMIT;');
626
+ tmpOut.push('EXIT');
627
+ tmpOut.push('');
628
+ return tmpOut.join('\n');
629
+ }
630
+
499
631
  // ---------------------------------------------------------------------------
500
632
  // CLI
501
633
  // ---------------------------------------------------------------------------
@@ -532,7 +664,10 @@ switch (tmpDialect)
532
664
  case 'mssql':
533
665
  process.stdout.write(emitMSSQL());
534
666
  break;
667
+ case 'oracle':
668
+ process.stdout.write(emitOracle());
669
+ break;
535
670
  default:
536
- process.stderr.write('Usage: bookstore-seed.js --dialect <mysql|postgresql|mssql>\n');
671
+ process.stderr.write('Usage: bookstore-seed.js --dialect <mysql|postgresql|mssql|oracle>\n');
537
672
  process.exit(1);
538
673
  }
@@ -14,6 +14,7 @@ echo ""
14
14
  "${SCRIPT_DIR}/mysql-test-db.sh" stop
15
15
  "${SCRIPT_DIR}/mssql-test-db.sh" stop
16
16
  "${SCRIPT_DIR}/postgresql-test-db.sh" stop
17
+ "${SCRIPT_DIR}/oracle-test-db.sh" stop
17
18
  "${SCRIPT_DIR}/mongodb-test-db.sh" stop
18
19
  "${SCRIPT_DIR}/solr-test-db.sh" stop
19
20
  "${SCRIPT_DIR}/dgraph-test-db.sh" stop
@@ -0,0 +1,128 @@
1
+ #!/bin/bash
2
+ # Oracle Test Database Management Script
3
+ #
4
+ # Usage:
5
+ # ./scripts/oracle-test-db.sh start - Start Oracle container, load schema and seed data
6
+ # ./scripts/oracle-test-db.sh stop - Stop and remove the container
7
+ # ./scripts/oracle-test-db.sh status - Check if the container is running
8
+ #
9
+ # Uses gvenzl/oracle-free (Oracle Database 23ai Free — no license required,
10
+ # multi-arch so it runs natively on Apple Silicon). gvenzl auto-creates the
11
+ # APP_USER in its own schema inside the FREEPDB1 pluggable database.
12
+ #
13
+ # The container settings match the test configuration in
14
+ # test/Meadow-Provider-Oracle_tests.js and meadow-connection-oracle:
15
+ # Host: 127.0.0.1, Port: 21521, Service: FREEPDB1
16
+ # User: bookstore, Password: Retold1234567890!
17
+
18
+ CONTAINER_NAME="meadow-oracle-test"
19
+ ORACLE_USER="bookstore"
20
+ ORACLE_PASSWORD="Retold1234567890!"
21
+ ORACLE_SERVICE="FREEPDB1"
22
+ ORACLE_PORT="21521"
23
+ ORACLE_IMAGE="gvenzl/oracle-free:23-slim"
24
+
25
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+ SEED_GENERATOR="${SCRIPT_DIR}/bookstore-seed.js"
27
+
28
+ start_oracle() {
29
+ # Check if container already exists
30
+ if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
31
+ if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
32
+ echo "Oracle test container is already running."
33
+ return 0
34
+ else
35
+ echo "Removing stopped container..."
36
+ docker rm "${CONTAINER_NAME}" > /dev/null 2>&1
37
+ fi
38
+ fi
39
+
40
+ echo "Starting Oracle test container (first boot pulls the image and initializes the DB; this can take several minutes)..."
41
+ docker run -d \
42
+ --name "${CONTAINER_NAME}" \
43
+ -e ORACLE_PASSWORD="${ORACLE_PASSWORD}" \
44
+ -e APP_USER="${ORACLE_USER}" \
45
+ -e APP_USER_PASSWORD="${ORACLE_PASSWORD}" \
46
+ -p "${ORACLE_PORT}:1521" \
47
+ "${ORACLE_IMAGE}"
48
+
49
+ if [ $? -ne 0 ]; then
50
+ echo "ERROR: Failed to start Oracle container."
51
+ exit 1
52
+ fi
53
+
54
+ echo "Waiting for Oracle to accept connections (gvenzl healthcheck)..."
55
+ RETRIES=120
56
+ until docker exec "${CONTAINER_NAME}" healthcheck.sh > /dev/null 2>&1; do
57
+ RETRIES=$((RETRIES - 1))
58
+ if [ $RETRIES -le 0 ]; then
59
+ echo "ERROR: Oracle failed to become ready in time."
60
+ docker logs "${CONTAINER_NAME}" 2>&1 | tail -20
61
+ exit 1
62
+ fi
63
+ echo " ...waiting (${RETRIES} retries left)"
64
+ sleep 5
65
+ done
66
+
67
+ # Load bookstore schema and seed data (GUIDs minted at generation time via fable-uuid)
68
+ if [ -f "${SEED_GENERATOR}" ]; then
69
+ echo "Loading bookstore schema and seed data..."
70
+ node "${SEED_GENERATOR}" --dialect oracle | \
71
+ docker exec -i "${CONTAINER_NAME}" sqlplus -S "${ORACLE_USER}/${ORACLE_PASSWORD}@//localhost:1521/${ORACLE_SERVICE}"
72
+ if [ $? -ne 0 ]; then
73
+ echo "WARNING: Failed to load seed data. Tests requiring pre-populated data may fail."
74
+ else
75
+ echo "Bookstore schema and seed data loaded successfully."
76
+ fi
77
+ else
78
+ echo "WARNING: Seed generator not found at ${SEED_GENERATOR}. Skipping schema/data loading."
79
+ fi
80
+
81
+ echo ""
82
+ echo "Oracle test database is ready!"
83
+ echo " Container: ${CONTAINER_NAME}"
84
+ echo " Host: 127.0.0.1:${ORACLE_PORT}"
85
+ echo " Service: ${ORACLE_SERVICE}"
86
+ echo " User: ${ORACLE_USER}"
87
+ echo " Password: ${ORACLE_PASSWORD}"
88
+ echo ""
89
+ echo "Run tests with: npm test"
90
+ }
91
+
92
+ stop_oracle() {
93
+ if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
94
+ echo "Stopping and removing Oracle test container..."
95
+ docker stop "${CONTAINER_NAME}" > /dev/null 2>&1
96
+ docker rm "${CONTAINER_NAME}" > /dev/null 2>&1
97
+ echo "Oracle test container removed."
98
+ else
99
+ echo "No Oracle test container found."
100
+ fi
101
+ }
102
+
103
+ status_oracle() {
104
+ if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
105
+ echo "Oracle test container is running."
106
+ docker ps --filter "name=${CONTAINER_NAME}" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
107
+ elif docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
108
+ echo "Oracle test container exists but is stopped."
109
+ else
110
+ echo "Oracle test container is not running."
111
+ fi
112
+ }
113
+
114
+ case "${1}" in
115
+ start)
116
+ start_oracle
117
+ ;;
118
+ stop)
119
+ stop_oracle
120
+ ;;
121
+ status)
122
+ status_oracle
123
+ ;;
124
+ *)
125
+ echo "Usage: $0 {start|stop|status}"
126
+ exit 1
127
+ ;;
128
+ esac
@@ -305,7 +305,11 @@ var MeadowProvider = function ()
305
305
  executeStatement(pQuery.query.body, tmpBinds, function (pError, pDBResult)
306
306
  {
307
307
  tmpResult.error = pError;
308
- tmpResult.value = pDBResult ? pDBResult.rowsAffected : 0;
308
+ // The Meadow Update behavior requires result.value to be an
309
+ // object; oracledb's UPDATE result has no rows array (no
310
+ // RETURNING), so mirror the PostgreSQL provider and expose an
311
+ // (empty) array. The post-update Read supplies the record.
312
+ tmpResult.value = (pDBResult && pDBResult.rows) ? pDBResult.rows : [];
309
313
  tmpResult.executed = true;
310
314
  return fCallback();
311
315
  });
@@ -0,0 +1,877 @@
1
+ /**
2
+ * Unit tests for the Meadow "Oracle" Provider
3
+ *
4
+ * These tests expect an Oracle database (see scripts/oracle-test-db.sh —
5
+ * gvenzl/oracle-free on port 21521). If meadow-connection-oracle is not
6
+ * installed, or no Oracle is reachable, the whole suite self-skips so a bare
7
+ * `npm test` (or test-all-providers without Oracle) doesn't hard-fail.
8
+ *
9
+ * @license MIT
10
+ *
11
+ * @author Steven Velozo <steven@velozo.com>
12
+ */
13
+
14
+ var Chai = require("chai");
15
+ var Expect = Chai.expect;
16
+
17
+ // Tolerate the connection module (and its oracledb dependency) being absent —
18
+ // the suite self-skips in that case rather than breaking the whole test run.
19
+ var libMeadowConnectionOracle = null;
20
+ try
21
+ {
22
+ libMeadowConnectionOracle = require('meadow-connection-oracle');
23
+ }
24
+ catch (pRequireError)
25
+ {
26
+ libMeadowConnectionOracle = null;
27
+ }
28
+
29
+ var tmpFableSettings = (
30
+ {
31
+ "Product": "MeadowOracleTestBookstore",
32
+ "ProductVersion": "1.0.0",
33
+
34
+ "UUID":
35
+ {
36
+ "DataCenter": 0,
37
+ "Worker": 0
38
+ },
39
+ "LogStreams":
40
+ [
41
+ {
42
+ "streamtype": "console"
43
+ }
44
+ ],
45
+
46
+ "Oracle":
47
+ {
48
+ "Server": "127.0.0.1",
49
+ "Port": 21521,
50
+ "User": "bookstore",
51
+ "Password": "Retold1234567890!",
52
+ "connectionType": "ServiceName",
53
+ "serviceName": "FREEPDB1",
54
+ "ConnectionPoolLimit": 5,
55
+ // Fail fast into a self-skip when no Oracle is reachable.
56
+ "ConnectRetryOptions": { "MaxAttempts": 1 },
57
+ "ConnectionTimeoutMs": 8000
58
+ }
59
+ });
60
+
61
+ var libFable = new (require('fable'))(tmpFableSettings);
62
+
63
+ if (libMeadowConnectionOracle)
64
+ {
65
+ libFable.serviceManager.addServiceType('MeadowOracleProvider', libMeadowConnectionOracle);
66
+ libFable.serviceManager.instantiateServiceProvider('MeadowOracleProvider');
67
+ }
68
+
69
+ var _AnimalJsonSchema = (
70
+ {
71
+ title: "Animal",
72
+ description: "A creature that lives in a meadow.",
73
+ type: "object",
74
+ properties: {
75
+ IDAnimal: {
76
+ description: "The unique identifier for an animal",
77
+ type: "integer"
78
+ },
79
+ Name: {
80
+ description: "The animal's name",
81
+ type: "string"
82
+ },
83
+ Type: {
84
+ description: "The type of the animal",
85
+ type: "string"
86
+ }
87
+ },
88
+ required: ["IDAnimal", "Name", "CreatingIDUser"]
89
+ });
90
+ var _AnimalSchema = (
91
+ [
92
+ { Column: "IDAnimal", Type: "AutoIdentity" },
93
+ { Column: "GUIDAnimal", Type: "AutoGUID" },
94
+ { Column: "CreateDate", Type: "CreateDate" },
95
+ { Column: "CreatingIDUser", Type: "CreateIDUser" },
96
+ { Column: "UpdateDate", Type: "UpdateDate" },
97
+ { Column: "UpdatingIDUser", Type: "UpdateIDUser" },
98
+ { Column: "Deleted", Type: "Deleted" },
99
+ { Column: "DeletingIDUser", Type: "DeleteIDUser" },
100
+ { Column: "DeleteDate", Type: "DeleteDate" },
101
+ { Column: "Name", Type: "String" },
102
+ { Column: "Type", Type: "String" },
103
+ { Column: "Metadata", Type:"JSON" },
104
+ { Column: "ExtraData", Type:"JSONProxy", StorageColumn:"ExtraDataJSON" }
105
+ ]);
106
+ var _AnimalDefault = (
107
+ {
108
+ IDAnimal: null,
109
+ GUIDAnimal: '',
110
+
111
+ CreateDate: false,
112
+ CreatingIDUser: 0,
113
+ UpdateDate: false,
114
+ UpdatingIDUser: 0,
115
+ Deleted: 0,
116
+ DeleteDate: false,
117
+ DeletingIDUser: 0,
118
+
119
+ Name: 'Unknown',
120
+ Type: 'Unclassified',
121
+
122
+ Metadata: {},
123
+ ExtraData: {}
124
+ });
125
+
126
+ suite
127
+ (
128
+ 'Meadow-Provider-Oracle',
129
+ function ()
130
+ {
131
+ var _SpooledUp = false;
132
+
133
+ // Oracle has no DROP TABLE IF EXISTS — swallow ORA-00942.
134
+ var _DropTableStatement = "BEGIN EXECUTE IMMEDIATE 'DROP TABLE FableTest CASCADE CONSTRAINTS'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -942 THEN RAISE; END IF; END;";
135
+
136
+ var _CreateTableStatement = 'CREATE TABLE FableTest ('
137
+ + ' IDAnimal NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,'
138
+ + " GUIDAnimal VARCHAR2(36) DEFAULT '00000000-0000-0000-0000-000000000000' NOT NULL,"
139
+ + ' CreateDate TIMESTAMP,'
140
+ + ' CreatingIDUser NUMBER DEFAULT 0 NOT NULL,'
141
+ + ' UpdateDate TIMESTAMP,'
142
+ + ' UpdatingIDUser NUMBER DEFAULT 0 NOT NULL,'
143
+ + ' Deleted NUMBER(1) DEFAULT 0 NOT NULL,'
144
+ + ' DeleteDate TIMESTAMP,'
145
+ + ' DeletingIDUser NUMBER DEFAULT 0 NOT NULL,'
146
+ + " Name VARCHAR2(128) DEFAULT ' ' NOT NULL,"
147
+ + " Type VARCHAR2(128) DEFAULT ' ' NOT NULL,"
148
+ + ' Metadata CLOB,'
149
+ + ' ExtraDataJSON CLOB'
150
+ + ' )';
151
+
152
+ var getAnimalInsert = function (pName, pType)
153
+ {
154
+ return "INSERT INTO FableTest (GUIDAnimal, CreateDate, CreatingIDUser, UpdateDate, UpdatingIDUser, Deleted, DeleteDate, DeletingIDUser, Name, Type, Metadata, ExtraDataJSON)"
155
+ + " VALUES ('00000000-0000-0000-0000-000000000000', SYS_EXTRACT_UTC(SYSTIMESTAMP), 1, SYS_EXTRACT_UTC(SYSTIMESTAMP), 1, 0, NULL, 0, '"
156
+ + pName + "', '" + pType + "', '{}', '{}')";
157
+ };
158
+
159
+ // Acquire a pooled connection, run a statement with autoCommit, and
160
+ // always release the connection (oracledb has no pool.query()).
161
+ var execOnPool = function (pSQL, fCallback)
162
+ {
163
+ var tmpProvider = libFable.MeadowOracleProvider;
164
+ tmpProvider.getConnection(function (pConnectionError, pConnection)
165
+ {
166
+ if (pConnectionError) { return fCallback(pConnectionError); }
167
+ pConnection.execute(pSQL, [], { autoCommit: true })
168
+ .then(function () { pConnection.close().then(() => fCallback()).catch(() => fCallback()); })
169
+ .catch(function (pError) { pConnection.close().then(() => fCallback(pError)).catch(() => fCallback(pError)); });
170
+ });
171
+ };
172
+
173
+ var newMeadow = function ()
174
+ {
175
+ return require('../source/Meadow.js')
176
+ .new(libFable, 'FableTest')
177
+ .setProvider('Oracle')
178
+ .setSchema(_AnimalSchema)
179
+ .setJsonSchema(_AnimalJsonSchema)
180
+ .setDefaultIdentifier('IDAnimal')
181
+ .setDefault(_AnimalDefault)
182
+ };
183
+
184
+ suiteSetup
185
+ (
186
+ function (fDone)
187
+ {
188
+ var tmpSelf = this;
189
+
190
+ // No connection module installed → skip the whole suite.
191
+ if (!libMeadowConnectionOracle)
192
+ {
193
+ return tmpSelf.skip();
194
+ }
195
+
196
+ if (_SpooledUp)
197
+ {
198
+ return fDone();
199
+ }
200
+
201
+ libFable.MeadowOracleProvider.connectAsync(
202
+ (pConnectError) =>
203
+ {
204
+ if (pConnectError)
205
+ {
206
+ // No reachable Oracle → skip the whole suite.
207
+ libFable.log.warn(`Oracle not reachable, skipping Meadow-Provider-Oracle suite: ${pConnectError}`);
208
+ return tmpSelf.skip();
209
+ }
210
+
211
+ libFable.log.info('Connection opened!');
212
+ libFable.Utility.waterfall(
213
+ [
214
+ function (fStageComplete)
215
+ {
216
+ execOnPool(_DropTableStatement, () => fStageComplete());
217
+ },
218
+ function (fStageComplete)
219
+ {
220
+ execOnPool(_CreateTableStatement, fStageComplete);
221
+ },
222
+ function (fStageComplete)
223
+ {
224
+ execOnPool(getAnimalInsert('Foo Foo', 'Bunny'), fStageComplete);
225
+ },
226
+ function (fStageComplete)
227
+ {
228
+ execOnPool(getAnimalInsert('Red Riding Hood', 'Girl'), fStageComplete);
229
+ },
230
+ function (fStageComplete)
231
+ {
232
+ execOnPool(getAnimalInsert('Red', 'Dog'), fStageComplete);
233
+ },
234
+ function (fStageComplete)
235
+ {
236
+ execOnPool(getAnimalInsert('Spot', 'Dog'), fStageComplete);
237
+ },
238
+ function (fStageComplete)
239
+ {
240
+ execOnPool(getAnimalInsert('Gertrude', 'Frog'), fStageComplete);
241
+ }
242
+ ],
243
+ function (pError)
244
+ {
245
+ if (pError)
246
+ {
247
+ libFable.log.error(`Error preparing Oracle test data: ${pError}`);
248
+ return fDone(pError);
249
+ }
250
+ _SpooledUp = true;
251
+ fDone();
252
+ }
253
+ );
254
+ }
255
+ );
256
+ }
257
+ );
258
+
259
+ suiteTeardown((fDone) =>
260
+ {
261
+ if (libFable.MeadowOracleProvider && libFable.MeadowOracleProvider.pool)
262
+ {
263
+ try
264
+ {
265
+ libFable.MeadowOracleProvider.pool.close(0).then(() => fDone()).catch(() => fDone());
266
+ }
267
+ catch (pError)
268
+ {
269
+ return fDone();
270
+ }
271
+ }
272
+ else
273
+ {
274
+ fDone();
275
+ }
276
+ });
277
+
278
+ suite
279
+ (
280
+ 'Object Sanity',
281
+ function ()
282
+ {
283
+ test
284
+ (
285
+ 'The Oracle class should initialize itself into a happy little object.',
286
+ function ()
287
+ {
288
+ var testMeadow = require('../source/Meadow.js').new(libFable).setProvider('Oracle');
289
+ Expect(testMeadow).to.be.an('object', 'Meadow should initialize as an object directly from the require statement.');
290
+ }
291
+ );
292
+ }
293
+ );
294
+ suite
295
+ (
296
+ 'Query Processing',
297
+ function ()
298
+ {
299
+ test
300
+ (
301
+ 'Create a record in the database',
302
+ function (fDone)
303
+ {
304
+ var testMeadow = newMeadow().setIDUser(90210);
305
+
306
+ var tmpQuery = testMeadow.query.clone().setLogLevel(5)
307
+ .addRecord({ Name: 'Blastoise', Type: 'Pokemon' });
308
+
309
+ testMeadow.doCreate(tmpQuery,
310
+ function (pError, pQuery, pQueryRead, pRecord)
311
+ {
312
+ Expect(pRecord.Name)
313
+ .to.equal('Blastoise');
314
+ Expect(pRecord.CreatingIDUser)
315
+ .to.equal(90210);
316
+ fDone();
317
+ }
318
+ )
319
+ }
320
+ );
321
+ test
322
+ (
323
+ 'Create a record with JSON data',
324
+ function (fDone)
325
+ {
326
+ var testMeadow = newMeadow().setIDUser(90210);
327
+
328
+ var tmpQuery = testMeadow.query.clone().setLogLevel(5)
329
+ .addRecord({ Name: 'Moose', Type: 'Mammal', Metadata: { habitat: 'forest', weight: 500 }, ExtraData: { endangered: false, population: 'stable' } });
330
+
331
+ testMeadow.doCreate(tmpQuery,
332
+ function (pError, pQuery, pQueryRead, pRecord)
333
+ {
334
+ Expect(pRecord.Name)
335
+ .to.equal('Moose');
336
+ Expect(pRecord.Metadata)
337
+ .to.be.an('object');
338
+ Expect(pRecord.Metadata.habitat)
339
+ .to.equal('forest');
340
+ Expect(pRecord.ExtraData)
341
+ .to.be.an('object');
342
+ Expect(pRecord.ExtraData.endangered)
343
+ .to.equal(false);
344
+ // The storage column should not be exposed on the marshaled record
345
+ Expect(pRecord).to.not.have.property('ExtraDataJSON');
346
+ fDone();
347
+ }
348
+ )
349
+ }
350
+ );
351
+ test
352
+ (
353
+ 'Read a record from the database',
354
+ function (fDone)
355
+ {
356
+ var testMeadow = newMeadow();
357
+
358
+ var tmpQuery = testMeadow.query
359
+ .addFilter('IDAnimal', 1);
360
+
361
+ testMeadow.doRead(tmpQuery,
362
+ function (pError, pQuery, pRecord)
363
+ {
364
+ Expect(pRecord.IDAnimal)
365
+ .to.equal(1);
366
+ Expect(pRecord.Name)
367
+ .to.equal('Foo Foo');
368
+ fDone();
369
+ }
370
+ )
371
+ }
372
+ );
373
+ test
374
+ (
375
+ 'Read all records from the database',
376
+ function (fDone)
377
+ {
378
+ var testMeadow = newMeadow();
379
+
380
+ testMeadow.doReads(testMeadow.query.addSort({Column: 'IDAnimal'}),
381
+ function (pError, pQuery, pRecords)
382
+ {
383
+ Expect(pRecords[0].IDAnimal)
384
+ .to.equal(1);
385
+ Expect(pRecords[0].Name)
386
+ .to.equal('Foo Foo');
387
+ Expect(pRecords[1].IDAnimal)
388
+ .to.equal(2);
389
+ Expect(pRecords[1].Name)
390
+ .to.equal('Red Riding Hood');
391
+ Expect(pRecords[1].Type)
392
+ .to.equal('Girl');
393
+ fDone();
394
+ }
395
+ )
396
+ }
397
+ );
398
+ test
399
+ (
400
+ 'Update a record in the database',
401
+ function (fDone)
402
+ {
403
+ var testMeadow = newMeadow();
404
+
405
+ var tmpQuery = testMeadow.query
406
+ .addRecord({ IDAnimal: 2, Type: 'Human' });
407
+
408
+ testMeadow.doUpdate(tmpQuery,
409
+ function (pError, pQuery, pQueryRead, pRecord)
410
+ {
411
+ Expect(pRecord.Type)
412
+ .to.equal('Human');
413
+ fDone();
414
+ }
415
+ )
416
+ }
417
+ );
418
+ test
419
+ (
420
+ 'Delete a record in the database',
421
+ function (fDone)
422
+ {
423
+ var testMeadow = newMeadow();
424
+
425
+ var tmpQuery = testMeadow.query.addFilter('IDAnimal', 3);
426
+
427
+ testMeadow.doDelete(tmpQuery,
428
+ function (pError, pQuery, pRecord)
429
+ {
430
+ // It returns the number of rows deleted
431
+ Expect(pRecord)
432
+ .to.equal(1);
433
+ fDone();
434
+ }
435
+ )
436
+ }
437
+ );
438
+ test
439
+ (
440
+ 'Undelete a record in the database',
441
+ function (fDone)
442
+ {
443
+ var testMeadow = newMeadow();
444
+
445
+ var tmpDeleteQuery = testMeadow.query.addFilter('IDAnimal', 5);
446
+
447
+ // Make sure the record is deleted!
448
+ testMeadow.doDelete(tmpDeleteQuery,
449
+ function (pDeleteError, pDeleteQuery, pDeleteRecord)
450
+ {
451
+ var tmpQuery = testMeadow.query.addFilter('IDAnimal', 5);
452
+ testMeadow.doUndelete(tmpQuery,
453
+ function (pError, pQuery, pRecord)
454
+ {
455
+ // It returns the number of rows undeleted
456
+ Expect(pRecord)
457
+ .to.equal(1);
458
+ fDone();
459
+ });
460
+ });
461
+ }
462
+ );
463
+ test
464
+ (
465
+ 'Count all records from the database',
466
+ function (fDone)
467
+ {
468
+ var testMeadow = newMeadow();
469
+
470
+ Expect(testMeadow.query.parameters.result.executed)
471
+ .to.equal(false);
472
+ testMeadow.doCount(testMeadow.query,
473
+ function (pError, pQuery, pRecord)
474
+ {
475
+ // There should be 6 records
476
+ Expect(pRecord)
477
+ .to.equal(6);
478
+ Expect(pQuery.parameters.result.executed)
479
+ .to.equal(true);
480
+ fDone();
481
+ }
482
+ )
483
+ }
484
+ );
485
+ test
486
+ (
487
+ 'Perform operations with a schema-based instantiation',
488
+ function (fDone)
489
+ {
490
+ var testMeadow = require('../source/Meadow.js').new(libFable)
491
+ .loadFromPackage(__dirname + '/Animal.json').setProvider('Oracle');
492
+
493
+ // Make sure the authentication stuff got loaded
494
+ Expect(testMeadow.schemaFull.authorizer.User)
495
+ .to.be.an('object');
496
+ Expect(testMeadow.schemaFull.authorizer.User.Create)
497
+ .to.equal('Allow');
498
+
499
+ var tmpQuery = testMeadow.query
500
+ .addRecord({ Name: 'Grommet', Type: 'Dog' });
501
+
502
+ testMeadow.doCreate(tmpQuery,
503
+ function (pError, pQuery, pQueryRead, pRecord)
504
+ {
505
+ Expect(pRecord.Name)
506
+ .to.equal('Grommet');
507
+ fDone();
508
+ }
509
+ )
510
+ }
511
+ );
512
+ }
513
+ );
514
+ suite
515
+ (
516
+ 'Logged Query Processing',
517
+ function ()
518
+ {
519
+ test
520
+ (
521
+ 'Create a record in the database',
522
+ function (fDone)
523
+ {
524
+ var testMeadow = newMeadow();
525
+
526
+ var tmpQuery = testMeadow.query
527
+ .setLogLevel(5)
528
+ .addRecord({ Name: 'MewTwo', Type: 'Pokemon' });
529
+
530
+ testMeadow.doCreate(tmpQuery,
531
+ function (pError, pQuery, pQueryRead, pRecord)
532
+ {
533
+ Expect(pRecord.Name)
534
+ .to.equal('MewTwo');
535
+ fDone();
536
+ }
537
+ )
538
+ }
539
+ );
540
+ test
541
+ (
542
+ 'Create a record in the database with a predefined GUID',
543
+ function (fDone)
544
+ {
545
+ var testMeadow = newMeadow();
546
+
547
+ var tmpQuery = testMeadow.query
548
+ .setLogLevel(5)
549
+ .addRecord({ Name: 'MewThree', GUIDAnimal: '0x12345', Type: 'Pokemon' });
550
+
551
+ testMeadow.doCreate(tmpQuery,
552
+ function (pError, pQuery, pQueryRead, pRecord)
553
+ {
554
+ Expect(pRecord.Name)
555
+ .to.equal('MewThree');
556
+ fDone();
557
+ }
558
+ )
559
+ }
560
+ );
561
+ test
562
+ (
563
+ 'Create a record in the database with a previously predefined GUID -- expect failure',
564
+ function (fDone)
565
+ {
566
+ var testMeadow = newMeadow();
567
+
568
+ var tmpQuery = testMeadow.query
569
+ .setLogLevel(5)
570
+ .addRecord({ Name: 'MewThree', GUIDAnimal: '0x12345', Type: 'Pokemon' });
571
+
572
+ testMeadow.doCreate(tmpQuery,
573
+ function (pError, pQuery, pQueryRead, pRecord)
574
+ {
575
+ Expect(pError)
576
+ .to.equal("Record with GUID 0x12345 already exists!");
577
+ fDone();
578
+ }
579
+ )
580
+ }
581
+ );
582
+ test
583
+ (
584
+ 'Read a record from the database',
585
+ function (fDone)
586
+ {
587
+ var testMeadow = newMeadow();
588
+
589
+ var tmpQuery = testMeadow.query
590
+ .setLogLevel(5)
591
+ .addFilter('IDAnimal', 1);
592
+
593
+ testMeadow.doRead(tmpQuery,
594
+ function (pError, pQuery, pRecord)
595
+ {
596
+ Expect(pRecord.IDAnimal)
597
+ .to.equal(1);
598
+ Expect(pRecord.Name)
599
+ .to.equal('Foo Foo');
600
+ fDone();
601
+ }
602
+ )
603
+ }
604
+ );
605
+ test
606
+ (
607
+ 'Read all records from the database',
608
+ function (fDone)
609
+ {
610
+ var testMeadow = newMeadow();
611
+
612
+ testMeadow.doReads(testMeadow.query.setLogLevel(5).addSort({Column: 'IDAnimal'}),
613
+ function (pError, pQuery, pRecords)
614
+ {
615
+ Expect(pRecords[0].IDAnimal)
616
+ .to.equal(1);
617
+ Expect(pRecords[0].Name)
618
+ .to.equal('Foo Foo');
619
+ Expect(pRecords[1].IDAnimal)
620
+ .to.equal(2);
621
+ Expect(pRecords[1].Name)
622
+ .to.equal('Red Riding Hood');
623
+ Expect(pRecords[1].Type)
624
+ .to.equal('Human');
625
+ fDone();
626
+ }
627
+ )
628
+ }
629
+ );
630
+ test
631
+ (
632
+ 'Update a record in the database',
633
+ function (fDone)
634
+ {
635
+ var testMeadow = newMeadow();
636
+
637
+ var tmpQuery = testMeadow.query
638
+ .setLogLevel(5)
639
+ .addRecord({ IDAnimal: 2, Type: 'HumanGirl' });
640
+
641
+ testMeadow.doUpdate(tmpQuery,
642
+ function (pError, pQuery, pQueryRead, pRecord)
643
+ {
644
+ Expect(pRecord.Type)
645
+ .to.equal('HumanGirl');
646
+ fDone();
647
+ }
648
+ )
649
+ }
650
+ );
651
+ test
652
+ (
653
+ 'Delete a record in the database',
654
+ function (fDone)
655
+ {
656
+ var testMeadow = newMeadow();
657
+
658
+ var tmpQuery = testMeadow.query
659
+ .setLogLevel(5)
660
+ .addFilter('IDAnimal', 4);
661
+
662
+ testMeadow.doDelete(tmpQuery,
663
+ function (pError, pQuery, pRecord)
664
+ {
665
+ // It returns the number of rows deleted
666
+ Expect(pRecord)
667
+ .to.equal(1);
668
+ fDone();
669
+ }
670
+ )
671
+ }
672
+ );
673
+ test
674
+ (
675
+ 'Count all records from the database',
676
+ function (fDone)
677
+ {
678
+ var testMeadow = newMeadow();
679
+
680
+ testMeadow.doCount(testMeadow.query.setLogLevel(5),
681
+ function (pError, pQuery, pRecord)
682
+ {
683
+ // There should be 8 records
684
+ Expect(pRecord)
685
+ .to.equal(8);
686
+ fDone();
687
+ }
688
+ )
689
+ }
690
+ );
691
+ test
692
+ (
693
+ 'Read a record from the database that had a defined GUID',
694
+ function (fDone)
695
+ {
696
+ var testMeadow = newMeadow();
697
+
698
+ var tmpQuery = testMeadow.query
699
+ .addFilter('GUIDAnimal', '0x12345');
700
+
701
+ testMeadow.doRead(tmpQuery,
702
+ function (pError, pQuery, pRecord)
703
+ {
704
+ Expect(pRecord.Name)
705
+ .to.equal('MewThree');
706
+ fDone();
707
+ }
708
+ )
709
+ }
710
+ );
711
+ test
712
+ (
713
+ 'Create a record in the database with a defined creating user',
714
+ function (fDone)
715
+ {
716
+ var testMeadow = newMeadow();
717
+ var tmpQuery = testMeadow.query
718
+ .setIDUser(800)
719
+ .addRecord({ Name: 'MewSix', GUIDAnimal: '0x123456', Type: 'Pokemon' });
720
+
721
+ testMeadow.doCreate(tmpQuery,
722
+ function (pError, pQuery, pQueryRead, pRecord)
723
+ {
724
+ Expect(pRecord.Name)
725
+ .to.equal('MewSix');
726
+ Expect(pRecord.CreatingIDUser)
727
+ .to.equal(800);
728
+ fDone();
729
+ }
730
+ )
731
+ }
732
+ );
733
+ }
734
+ );
735
+ suite
736
+ (
737
+ 'The Bad Kind of Query Processing',
738
+ function ()
739
+ {
740
+ test
741
+ (
742
+ 'Create a record in the database with no record',
743
+ function (fDone)
744
+ {
745
+ var testMeadow = newMeadow().setDefaultIdentifier('Type');
746
+
747
+ testMeadow.doCreate(testMeadow.query,
748
+ function (pError, pQuery, pQueryRead, pRecord)
749
+ {
750
+ Expect(pError)
751
+ .to.equal('No record submitted');
752
+ fDone();
753
+ }
754
+ )
755
+ }
756
+ );
757
+ test
758
+ (
759
+ 'Read a record from the database with no data returned',
760
+ function (fDone)
761
+ {
762
+ var testMeadow = newMeadow();
763
+
764
+ var tmpQuery = testMeadow.query
765
+ .addFilter('IDAnimal', 5000);
766
+ testMeadow.doRead(tmpQuery,
767
+ function (pError, pQuery, pRecord)
768
+ {
769
+ Expect(pRecord)
770
+ .to.equal(false);
771
+ fDone();
772
+ }
773
+ )
774
+ }
775
+ );
776
+ test
777
+ (
778
+ 'Read records from the database with no data returned',
779
+ function (fDone)
780
+ {
781
+ var testMeadow = newMeadow();
782
+
783
+ var tmpQuery = testMeadow.query
784
+ .addFilter('IDAnimal', 5000);
785
+
786
+ testMeadow.doReads(tmpQuery,
787
+ function (pError, pQuery, pRecord)
788
+ {
789
+ Expect(pRecord.length)
790
+ .to.equal(0);
791
+ fDone();
792
+ }
793
+ )
794
+ }
795
+ );
796
+ test
797
+ (
798
+ 'Update a record in the database with a bad filter',
799
+ function (fDone)
800
+ {
801
+ var testMeadow = newMeadow();
802
+
803
+ var tmpQuery = testMeadow.query
804
+ .setLogLevel(5)
805
+ .addRecord({ IDAnimal: undefined, Type: 'HumanGirl' });
806
+
807
+ testMeadow.doUpdate(tmpQuery,
808
+ function (pError, pQuery, pQueryRead, pRecord)
809
+ {
810
+ Expect(pError)
811
+ .to.equal('Automated update missing filters... aborting!');
812
+ fDone();
813
+ }
814
+ )
815
+ }
816
+ );
817
+ test
818
+ (
819
+ 'Update a record in the database without passing a record in',
820
+ function (fDone)
821
+ {
822
+ var testMeadow = newMeadow();
823
+
824
+ testMeadow.doUpdate(testMeadow.query,
825
+ function (pError, pQuery, pQueryRead, pRecord)
826
+ {
827
+ Expect(pError)
828
+ .to.equal('No record submitted');
829
+ fDone();
830
+ }
831
+ )
832
+ }
833
+ );
834
+ test
835
+ (
836
+ 'Update a record in the database with a bad record passed in (no default identifier)',
837
+ function (fDone)
838
+ {
839
+ var testMeadow = newMeadow();
840
+
841
+ var tmpQuery = testMeadow.query
842
+ .addRecord({ Name: 'Bill' });
843
+
844
+ testMeadow.doUpdate(tmpQuery,
845
+ function (pError, pQuery, pQueryRead, pRecord)
846
+ {
847
+ Expect(pError)
848
+ .to.equal('Automated update missing default identifier');
849
+ fDone();
850
+ }
851
+ )
852
+ }
853
+ );
854
+ test
855
+ (
856
+ 'Update a record in the database that does not exist',
857
+ function (fDone)
858
+ {
859
+ var testMeadow = newMeadow();
860
+
861
+ var tmpQuery = testMeadow.query
862
+ .addRecord({ IDAnimal: 983924 });
863
+
864
+ testMeadow.doUpdate(tmpQuery,
865
+ function (pError, pQuery, pQueryRead, pRecord)
866
+ {
867
+ Expect(pError)
868
+ .to.equal('No record found to update!');
869
+ fDone();
870
+ }
871
+ )
872
+ }
873
+ );
874
+ }
875
+ );
876
+ }
877
+ );