|
1 | 1 | import { expect } from "chai"; |
2 | 2 | import { Database } from "../databases.js"; |
3 | | -import { Graph } from "../graphs.js"; |
| 3 | +import { isArangoError } from "../errors.js"; |
| 4 | +import { Graph, type CreateGraphOptions } from "../graphs.js"; |
| 5 | +import { GRAPH_NOT_FOUND } from "../lib/codes.js"; |
| 6 | +import { versionStringToArangoVersionCode } from "./_arango-server-version.js"; |
4 | 7 | import { config } from "./_config.js"; |
5 | 8 | import { |
6 | 9 | clusterIntegrationTimeoutMs, |
@@ -123,6 +126,153 @@ describe("Graph API", function () { |
123 | 126 | expect(data).to.have.property("name", graph.name); |
124 | 127 | }); |
125 | 128 | }); |
| 129 | + describe("graph.create advanced graph options", function () { |
| 130 | + let graph: Graph; |
| 131 | + let collectionNames: string[]; |
| 132 | + let sequence = 0; |
| 133 | + |
| 134 | + before(async function () { |
| 135 | + const version = await system.version(); |
| 136 | + const { role } = await system.request<{ role: string }>({ |
| 137 | + pathname: "/_admin/server/role", |
| 138 | + }); |
| 139 | + // Community Edition includes these features from 3.12.5 onward. |
| 140 | + // https://docs.arango.ai/arangodb/3.12/release-notes/version-3.12/whats-new-in-3-12/ |
| 141 | + if ( |
| 142 | + role !== "COORDINATOR" || |
| 143 | + (version.license !== "enterprise" && |
| 144 | + versionStringToArangoVersionCode(version.version) < 31205) |
| 145 | + ) { |
| 146 | + // eslint-disable-next-line no-console |
| 147 | + console.warn( |
| 148 | + `Skipping advanced graph creation: ArangoDB ${version.version}, ${version.license}, role ${role} requires a cluster with smart graph support.`, |
| 149 | + ); |
| 150 | + this.skip(); |
| 151 | + } |
| 152 | + }); |
| 153 | + |
| 154 | + beforeEach(() => { |
| 155 | + const prefix = `graph_options_${Date.now()}_${sequence++}`; |
| 156 | + graph = db.graph(prefix); |
| 157 | + collectionNames = ["edges", "start", "end", "orphan"].map( |
| 158 | + (suffix) => `${prefix}_${suffix}`, |
| 159 | + ); |
| 160 | + }); |
| 161 | + |
| 162 | + afterEach(async () => { |
| 163 | + try { |
| 164 | + await graph.drop(true); |
| 165 | + } catch (error) { |
| 166 | + if (!isArangoError(error) || error.errorNum !== GRAPH_NOT_FOUND) { |
| 167 | + throw error; |
| 168 | + } |
| 169 | + } finally { |
| 170 | + // A failed create can leave collections without a graph definition. |
| 171 | + for (const name of collectionNames) { |
| 172 | + const collection = db.collection(name); |
| 173 | + if (await collection.exists()) await collection.drop(); |
| 174 | + } |
| 175 | + } |
| 176 | + }); |
| 177 | + |
| 178 | + const cases: { |
| 179 | + title: string; |
| 180 | + options: CreateGraphOptions; |
| 181 | + expected: Record<string, unknown>; |
| 182 | + satellite?: boolean; |
| 183 | + }[] = [ |
| 184 | + { |
| 185 | + title: "creates an EnterpriseGraph without a smart attribute", |
| 186 | + options: { isSmart: true }, |
| 187 | + expected: { isSmart: true, isSatellite: false, isDisjoint: false }, |
| 188 | + }, |
| 189 | + { |
| 190 | + title: "creates a SmartGraph with explicit isSmart", |
| 191 | + options: { isSmart: true, smartGraphAttribute: "region" }, |
| 192 | + expected: { |
| 193 | + isSmart: true, |
| 194 | + isSatellite: false, |
| 195 | + smartGraphAttribute: "region", |
| 196 | + isDisjoint: false, |
| 197 | + }, |
| 198 | + }, |
| 199 | + { |
| 200 | + title: "creates a SmartGraph from the smart attribute alone", |
| 201 | + options: { smartGraphAttribute: "region" }, |
| 202 | + expected: { |
| 203 | + isSmart: true, |
| 204 | + isSatellite: false, |
| 205 | + smartGraphAttribute: "region", |
| 206 | + isDisjoint: false, |
| 207 | + }, |
| 208 | + }, |
| 209 | + { |
| 210 | + title: "creates a Disjoint SmartGraph", |
| 211 | + options: { |
| 212 | + isSmart: true, |
| 213 | + smartGraphAttribute: "region", |
| 214 | + isDisjoint: true, |
| 215 | + }, |
| 216 | + expected: { |
| 217 | + isSmart: true, |
| 218 | + isSatellite: false, |
| 219 | + smartGraphAttribute: "region", |
| 220 | + isDisjoint: true, |
| 221 | + }, |
| 222 | + }, |
| 223 | + { |
| 224 | + title: "creates a SmartGraph with a satellite vertex collection", |
| 225 | + options: { isSmart: true, smartGraphAttribute: "region" }, |
| 226 | + expected: { |
| 227 | + isSmart: true, |
| 228 | + isSatellite: false, |
| 229 | + smartGraphAttribute: "region", |
| 230 | + }, |
| 231 | + satellite: true, |
| 232 | + }, |
| 233 | + { |
| 234 | + title: "creates a SatelliteGraph when isSmart is also true", |
| 235 | + options: { isSmart: true, replicationFactor: "satellite" }, |
| 236 | + expected: { isSmart: false, isSatellite: true }, |
| 237 | + }, |
| 238 | + ]; |
| 239 | + |
| 240 | + for (const { title, options, expected, satellite } of cases) { |
| 241 | + it(title, async () => { |
| 242 | + const [edges, start, end, orphan] = collectionNames; |
| 243 | + const created = await graph.create( |
| 244 | + [{ collection: edges, from: [start], to: [end] }], |
| 245 | + { |
| 246 | + ...(options.replicationFactor === "satellite" |
| 247 | + ? {} |
| 248 | + : { numberOfShards: 2, replicationFactor: 1 }), |
| 249 | + ...options, |
| 250 | + orphanCollections: [orphan], |
| 251 | + ...(satellite ? { satellites: [db.collection(end)] } : {}), |
| 252 | + }, |
| 253 | + ); |
| 254 | + await db.waitForPropagation( |
| 255 | + { pathname: `/_api/gharial/${graph.name}` }, |
| 256 | + propagationForResourceMs, |
| 257 | + ); |
| 258 | + for (const data of [created, await graph.get()]) { |
| 259 | + expect(data).to.include({ name: graph.name, ...expected }); |
| 260 | + expect(data.orphanCollections).to.include(orphan); |
| 261 | + if (!options.smartGraphAttribute) { |
| 262 | + expect(data.smartGraphAttribute).to.be.undefined; |
| 263 | + } |
| 264 | + } |
| 265 | + if (satellite) { |
| 266 | + await db.waitForPropagation( |
| 267 | + { pathname: `/_api/collection/${end}` }, |
| 268 | + propagationForResourceMs, |
| 269 | + ); |
| 270 | + const properties = await db.collection(end).properties(); |
| 271 | + expect(properties.replicationFactor).to.equal("satellite"); |
| 272 | + } |
| 273 | + }); |
| 274 | + } |
| 275 | + }); |
126 | 276 | describe("graph.drop", () => { |
127 | 277 | let graph: Graph; |
128 | 278 | let edgeCollectionNames: string[]; |
|
0 commit comments