@firedrill-tools/linkedin 0.1.1
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/LICENSE +201 -0
- package/README.md +189 -0
- package/firedrill/agent.target.json +16 -0
- package/firedrill/baseline.scenario.json +1834 -0
- package/firedrill/comment-throttled.scenario.json +11 -0
- package/firedrill/conformance.suite.json +27 -0
- package/firedrill/linkedin-comment-throttled.drill.json +51 -0
- package/firedrill/linkedin-comments-connection.drill.json +43 -0
- package/firedrill/linkedin-comments.drill.json +196 -0
- package/firedrill/linkedin-denied.drill.json +383 -0
- package/firedrill/linkedin-fresh-install.drill.json +43 -0
- package/firedrill/linkedin-identity.drill.json +73 -0
- package/firedrill/linkedin-invalid-token.drill.json +393 -0
- package/firedrill/linkedin-limited-scopes.drill.json +88 -0
- package/firedrill/linkedin-openid-only.drill.json +383 -0
- package/firedrill/linkedin-orgs-connection.drill.json +28 -0
- package/firedrill/linkedin-orgs-network.drill.json +193 -0
- package/firedrill/linkedin-post-lost-response.drill.json +61 -0
- package/firedrill/linkedin-posts-lifecycle.drill.json +228 -0
- package/firedrill/linkedin-reactions-metadata.drill.json +196 -0
- package/firedrill/linkedin-reactions-outage.drill.json +66 -0
- package/firedrill/linkedin-tight-limits.drill.json +228 -0
- package/firedrill/linkedin-ugc-legacy.drill.json +43 -0
- package/firedrill/linkedin-visibility-connection.drill.json +73 -0
- package/firedrill/linkedin-visibility-stranger.drill.json +88 -0
- package/firedrill/linkedin-wire-errors.drill.json +353 -0
- package/firedrill/post-lost-response.scenario.json +11 -0
- package/firedrill/reactions-outage.scenario.json +11 -0
- package/firedrill/tight-limits.scenario.json +16 -0
- package/firedrill/tools/linkedin/app/assets/ATTRIBUTION.md +28 -0
- package/firedrill/tools/linkedin/app/assets/fonts/OFL.txt +93 -0
- package/firedrill/tools/linkedin/app/assets/linkedin-in.svg +1 -0
- package/firedrill/tools/linkedin/app/assets/linkedin-wordmark.svg +1 -0
- package/firedrill/tools/linkedin/app/assets/linkedin.svg +1 -0
- package/firedrill/tools/linkedin/app/site/app.js +80 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-400-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-600-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-700-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-400-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-600-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-700-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/linkedin-wordmark.svg +1 -0
- package/firedrill/tools/linkedin/app/site/assets/linkedin.svg +1 -0
- package/firedrill/tools/linkedin/app/site/base.css +87 -0
- package/firedrill/tools/linkedin/app/site/comment-box.js +119 -0
- package/firedrill/tools/linkedin/app/site/comments.css +59 -0
- package/firedrill/tools/linkedin/app/site/comments.js +188 -0
- package/firedrill/tools/linkedin/app/site/company.js +115 -0
- package/firedrill/tools/linkedin/app/site/composer.js +112 -0
- package/firedrill/tools/linkedin/app/site/feed.css +105 -0
- package/firedrill/tools/linkedin/app/site/feed.js +134 -0
- package/firedrill/tools/linkedin/app/site/icons.js +70 -0
- package/firedrill/tools/linkedin/app/site/index.html +58 -0
- package/firedrill/tools/linkedin/app/site/nav.css +51 -0
- package/firedrill/tools/linkedin/app/site/network.js +77 -0
- package/firedrill/tools/linkedin/app/site/overlay.css +62 -0
- package/firedrill/tools/linkedin/app/site/overlay.js +108 -0
- package/firedrill/tools/linkedin/app/site/pages.css +78 -0
- package/firedrill/tools/linkedin/app/site/post-actions.js +83 -0
- package/firedrill/tools/linkedin/app/site/post-menu.js +72 -0
- package/firedrill/tools/linkedin/app/site/post.css +82 -0
- package/firedrill/tools/linkedin/app/site/post.js +131 -0
- package/firedrill/tools/linkedin/app/site/profile.js +139 -0
- package/firedrill/tools/linkedin/app/site/reactions.js +46 -0
- package/firedrill/tools/linkedin/app/site/reactors.js +71 -0
- package/firedrill/tools/linkedin/app/site/shell.js +107 -0
- package/firedrill/tools/linkedin/app/site/store.js +62 -0
- package/firedrill/tools/linkedin/app/site/text.js +80 -0
- package/firedrill/tools/linkedin/app/site/ui.js +142 -0
- package/firedrill/tools/linkedin/behavior.mjs +66 -0
- package/firedrill/tools/linkedin/lib/access.mjs +69 -0
- package/firedrill/tools/linkedin/lib/auth.mjs +115 -0
- package/firedrill/tools/linkedin/lib/cascade.mjs +28 -0
- package/firedrill/tools/linkedin/lib/errors.mjs +50 -0
- package/firedrill/tools/linkedin/lib/message.mjs +43 -0
- package/firedrill/tools/linkedin/lib/paging.mjs +54 -0
- package/firedrill/tools/linkedin/lib/postinput.mjs +47 -0
- package/firedrill/tools/linkedin/lib/render.mjs +95 -0
- package/firedrill/tools/linkedin/lib/routes.mjs +219 -0
- package/firedrill/tools/linkedin/lib/social.mjs +25 -0
- package/firedrill/tools/linkedin/lib/store.mjs +54 -0
- package/firedrill/tools/linkedin/lib/text.mjs +55 -0
- package/firedrill/tools/linkedin/lib/urn.mjs +93 -0
- package/firedrill/tools/linkedin/lib/wire.mjs +125 -0
- package/firedrill/tools/linkedin/linkedin.tool.json +8935 -0
- package/firedrill/tools/linkedin/ops/comments-read.mjs +57 -0
- package/firedrill/tools/linkedin/ops/comments-write.mjs +104 -0
- package/firedrill/tools/linkedin/ops/feed.mjs +74 -0
- package/firedrill/tools/linkedin/ops/network.mjs +46 -0
- package/firedrill/tools/linkedin/ops/orgs.mjs +93 -0
- package/firedrill/tools/linkedin/ops/posts-edit.mjs +71 -0
- package/firedrill/tools/linkedin/ops/posts-read.mjs +54 -0
- package/firedrill/tools/linkedin/ops/posts-write.mjs +74 -0
- package/firedrill/tools/linkedin/ops/profile.mjs +38 -0
- package/firedrill/tools/linkedin/ops/reactions.mjs +78 -0
- package/firedrill/tools/linkedin/ops/social.mjs +34 -0
- package/firedrill/world.json +2585 -0
- package/firedrill.json +5 -0
- package/package.json +52 -0
- package/starter.json +1833 -0
- package/test/client.mjs +71 -0
- package/test/conformance.mjs +20 -0
- package/test/fixtures.mjs +182 -0
- package/test/flows/comments.mjs +116 -0
- package/test/flows/every.mjs +38 -0
- package/test/flows/faults.mjs +56 -0
- package/test/flows/identity.mjs +70 -0
- package/test/flows/orgs.mjs +107 -0
- package/test/flows/posts.mjs +196 -0
- package/test/flows/reactions.mjs +101 -0
- package/test/flows/wire.mjs +62 -0
package/test/client.mjs
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// HTTP helpers for the LinkedIn conformance target (Node built-ins only).
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
const BASE = process.env.FIREDRILL_HTTP_URL;
|
|
5
|
+
const TOKEN = process.env.FIREDRILL_HTTP_TOKEN;
|
|
6
|
+
assert.ok(BASE && TOKEN, "the HTTP binding is required (FIREDRILL_HTTP_URL / FIREDRILL_HTTP_TOKEN)");
|
|
7
|
+
|
|
8
|
+
export const VERSION = "202608";
|
|
9
|
+
|
|
10
|
+
/** Percent-encodes a URN or key value for a path segment (parentheses and commas included). */
|
|
11
|
+
export const enc = (value) => encodeURIComponent(value).replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
12
|
+
export const personUrn = (id) => `urn:li:person:${id}`;
|
|
13
|
+
export const orgUrn = (id) => `urn:li:organization:${id}`;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Calls a LinkedIn route. `expect` is a status or [status, code]. `/rest` paths send LinkedIn-Version unless `version: null`.
|
|
17
|
+
* Error bodies are checked against LinkedIn's envelope: `/rest` carries `code`, `/v2` does not.
|
|
18
|
+
*/
|
|
19
|
+
export async function call(method, path, expect = 200, { json, headers = {}, version = VERSION, restli } = {}) {
|
|
20
|
+
const [status, code] = Array.isArray(expect) ? expect : [expect];
|
|
21
|
+
const rest = path.startsWith("/rest/");
|
|
22
|
+
const init = { method, headers: { authorization: `Bearer ${TOKEN}`, "x-restli-protocol-version": "2.0.0", ...headers } };
|
|
23
|
+
if (rest && version !== null) init.headers["linkedin-version"] = version;
|
|
24
|
+
if (restli !== undefined) init.headers["x-restli-method"] = restli;
|
|
25
|
+
if (json !== undefined) {
|
|
26
|
+
init.headers["content-type"] = "application/json";
|
|
27
|
+
init.body = typeof json === "string" ? json : JSON.stringify(json);
|
|
28
|
+
}
|
|
29
|
+
const response = await fetch(`${BASE}${path}`, init);
|
|
30
|
+
const text = await response.text();
|
|
31
|
+
assert.equal(response.status, status, `${method} ${path} → ${response.status} ${text.slice(0, 600)}`);
|
|
32
|
+
let body;
|
|
33
|
+
if (status === 204 || status === 202 || (status === 201 && text === "")) assert.equal(text, "", `${method} ${path}: ${status} has no body`);
|
|
34
|
+
else body = JSON.parse(text);
|
|
35
|
+
if (status >= 400) {
|
|
36
|
+
assert.equal(body.status, status, `${method} ${path}: envelope status`);
|
|
37
|
+
assert.equal(typeof body.serviceErrorCode, "number");
|
|
38
|
+
assert.equal(typeof body.message, "string");
|
|
39
|
+
if (rest) {
|
|
40
|
+
assert.equal(typeof body.code, "string", `${method} ${path}: /rest envelope carries code`);
|
|
41
|
+
if (code !== undefined) assert.equal(body.code, code, `${method} ${path}: expected ${code}, got ${body.code} (${body.message})`);
|
|
42
|
+
} else {
|
|
43
|
+
assert.equal(body.code, undefined, `${method} ${path}: /v2 envelope has no code`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (status < 500 && response.status !== 404) assert.equal(response.headers.get("x-restli-protocol-version"), "2.0.0");
|
|
47
|
+
return { status: response.status, body, headers: response.headers };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const get = (path, expect, options) => call("GET", path, expect, options);
|
|
51
|
+
export const post = (path, json, expect = 201, options = {}) => call("POST", path, expect, { ...options, json });
|
|
52
|
+
export const partial = (path, json, expect = 204, options = {}) => call("POST", path, expect, { restli: "PARTIAL_UPDATE", ...options, json });
|
|
53
|
+
export const del = (path, expect = 204, options = {}) => call("DELETE", path, expect, options);
|
|
54
|
+
|
|
55
|
+
/** Canonical operation call. `expect` is "ok", "denied", "invalid" or a declared error code. Returns the outcome. */
|
|
56
|
+
export async function op(operationId, args = {}, expect = "ok") {
|
|
57
|
+
const response = await fetch(`${BASE}/v1/operations/linkedin/${operationId}`, {
|
|
58
|
+
method: "POST", headers: { authorization: `Bearer ${TOKEN}`, "content-type": "application/json" }, body: JSON.stringify({ arguments: args }),
|
|
59
|
+
});
|
|
60
|
+
const record = await response.json();
|
|
61
|
+
const outcome = record.outcome ?? {};
|
|
62
|
+
const got = outcome.status === "tool_error" ? String(outcome.error?.code ?? "").split(".").pop() : outcome.status;
|
|
63
|
+
assert.equal(got, expect, `${operationId} ${JSON.stringify(args).slice(0, 300)} → ${JSON.stringify(outcome).slice(0, 600)}`);
|
|
64
|
+
return outcome;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A text post body for POST /rest/posts. */
|
|
68
|
+
export const textPost = (author, commentary, extra = {}) => ({
|
|
69
|
+
author, commentary, visibility: "PUBLIC", distribution: { feedDistribution: "MAIN_FEED", targetEntities: [], thirdPartyDistributionChannels: [] },
|
|
70
|
+
lifecycleState: "PUBLISHED", isReshareDisabledByAuthor: false, ...extra,
|
|
71
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// LinkedIn Tool conformance target: a scripted Tool test, not a model-driven agent. Node built-ins only.
|
|
2
|
+
// Calls the LinkedIn-shaped routes (Bearer token, LinkedIn-Version header) and canonical operations at FIREDRILL_HTTP_URL.
|
|
3
|
+
// Each drill instruction names one flow ("Run the <flow> flow ..."); every flow throws on any unexpected status, header or body.
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { FLOWS as COMMENTS } from "./flows/comments.mjs";
|
|
6
|
+
import { FLOWS as FAULTS } from "./flows/faults.mjs";
|
|
7
|
+
import { FLOWS as IDENTITY } from "./flows/identity.mjs";
|
|
8
|
+
import { FLOWS as ORGS } from "./flows/orgs.mjs";
|
|
9
|
+
import { FLOWS as POSTS } from "./flows/posts.mjs";
|
|
10
|
+
import { FLOWS as REACTIONS } from "./flows/reactions.mjs";
|
|
11
|
+
import { FLOWS as WIRE } from "./flows/wire.mjs";
|
|
12
|
+
|
|
13
|
+
let task = "";
|
|
14
|
+
for await (const chunk of process.stdin) task += chunk;
|
|
15
|
+
const instruction = String(JSON.parse(task).instruction ?? "");
|
|
16
|
+
const flow = /Run the ([a-z-]+) flow/.exec(instruction)?.[1];
|
|
17
|
+
const FLOWS = { ...IDENTITY, ...POSTS, ...COMMENTS, ...REACTIONS, ...ORGS, ...WIRE, ...FAULTS };
|
|
18
|
+
assert.ok(flow !== undefined && Object.hasOwn(FLOWS, flow), `unknown flow in instruction: ${instruction}`);
|
|
19
|
+
await FLOWS[flow]();
|
|
20
|
+
console.log(JSON.stringify({ flow, status: "passed" }));
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Generated from the starter data (do not edit by hand): seeded ids used by the conformance flows.
|
|
2
|
+
export const PEOPLE = {"maya":"mOk4f0rPx1","sam":"sWh1tf1eld","priya":"pR4manQ7xz","luis":"lC4rv4lho0","zoe":"zLuk4s1ew1","daniel":"dMens4hK2p","aiko":"aT4nak4Rr8","grace":"gOduy4Lm3n","jordan":"jR3yesVv0q","elena":"ePetr0v4Qs","tomas":"tIb4n3zX0p","noor":"nH4dd4dPq2"};
|
|
3
|
+
export const ORGS = {"northwind":81234567,"brightline":81234568,"lakeview":81234569};
|
|
4
|
+
export const POSTS = {
|
|
5
|
+
"mayaDeleted": {
|
|
6
|
+
"id": "7495479617126400001",
|
|
7
|
+
"urn": "urn:li:share:7495479617126400001",
|
|
8
|
+
"activity": "urn:li:activity:7495479617126400002",
|
|
9
|
+
"activityId": "7495479617126400002"
|
|
10
|
+
},
|
|
11
|
+
"sam": {
|
|
12
|
+
"id": "7496566780723200003",
|
|
13
|
+
"urn": "urn:li:share:7496566780723200003",
|
|
14
|
+
"activity": "urn:li:activity:7496566780723200004",
|
|
15
|
+
"activityId": "7496566780723200004"
|
|
16
|
+
},
|
|
17
|
+
"mayaReshare": {
|
|
18
|
+
"id": "7498378720051200005",
|
|
19
|
+
"urn": "urn:li:share:7498378720051200005",
|
|
20
|
+
"activity": "urn:li:activity:7498378720051200006",
|
|
21
|
+
"activityId": "7498378720051200006"
|
|
22
|
+
},
|
|
23
|
+
"mayaUgc": {
|
|
24
|
+
"id": "7500190659379200007",
|
|
25
|
+
"urn": "urn:li:ugcPost:7500190659379200007",
|
|
26
|
+
"activity": "urn:li:activity:7500190659379200008",
|
|
27
|
+
"activityId": "7500190659379200008"
|
|
28
|
+
},
|
|
29
|
+
"mayaLong": {
|
|
30
|
+
"id": "7501277822976000009",
|
|
31
|
+
"urn": "urn:li:share:7501277822976000009",
|
|
32
|
+
"activity": "urn:li:activity:7501277822976000010",
|
|
33
|
+
"activityId": "7501277822976000010"
|
|
34
|
+
},
|
|
35
|
+
"mayaClosed": {
|
|
36
|
+
"id": "7502002598707200011",
|
|
37
|
+
"urn": "urn:li:share:7502002598707200011",
|
|
38
|
+
"activity": "urn:li:activity:7502002598707200012",
|
|
39
|
+
"activityId": "7502002598707200012"
|
|
40
|
+
},
|
|
41
|
+
"brightline": {
|
|
42
|
+
"id": "7502364986572800013",
|
|
43
|
+
"urn": "urn:li:share:7502364986572800013",
|
|
44
|
+
"activity": "urn:li:activity:7502364986572800014",
|
|
45
|
+
"activityId": "7502364986572800014"
|
|
46
|
+
},
|
|
47
|
+
"mayaEdited": {
|
|
48
|
+
"id": "7502727374438400015",
|
|
49
|
+
"urn": "urn:li:share:7502727374438400015",
|
|
50
|
+
"activity": "urn:li:activity:7502727374438400016",
|
|
51
|
+
"activityId": "7502727374438400016"
|
|
52
|
+
},
|
|
53
|
+
"northwindArticle": {
|
|
54
|
+
"id": "7503089762304000017",
|
|
55
|
+
"urn": "urn:li:share:7503089762304000017",
|
|
56
|
+
"activity": "urn:li:activity:7503089762304000018",
|
|
57
|
+
"activityId": "7503089762304000018"
|
|
58
|
+
},
|
|
59
|
+
"mayaLoggedIn": {
|
|
60
|
+
"id": "7503452150169600019",
|
|
61
|
+
"urn": "urn:li:share:7503452150169600019",
|
|
62
|
+
"activity": "urn:li:activity:7503452150169600020",
|
|
63
|
+
"activityId": "7503452150169600020"
|
|
64
|
+
},
|
|
65
|
+
"mayaConnections": {
|
|
66
|
+
"id": "7503814538035200021",
|
|
67
|
+
"urn": "urn:li:share:7503814538035200021",
|
|
68
|
+
"activity": "urn:li:activity:7503814538035200022",
|
|
69
|
+
"activityId": "7503814538035200022"
|
|
70
|
+
},
|
|
71
|
+
"mayaMention": {
|
|
72
|
+
"id": "7504176925900800023",
|
|
73
|
+
"urn": "urn:li:share:7504176925900800023",
|
|
74
|
+
"activity": "urn:li:activity:7504176925900800024",
|
|
75
|
+
"activityId": "7504176925900800024"
|
|
76
|
+
},
|
|
77
|
+
"jordan": {
|
|
78
|
+
"id": "7504176925900800025",
|
|
79
|
+
"urn": "urn:li:share:7504176925900800025",
|
|
80
|
+
"activity": "urn:li:activity:7504176925900800026",
|
|
81
|
+
"activityId": "7504176925900800026"
|
|
82
|
+
},
|
|
83
|
+
"mayaHashtags": {
|
|
84
|
+
"id": "7504539313766400027",
|
|
85
|
+
"urn": "urn:li:share:7504539313766400027",
|
|
86
|
+
"activity": "urn:li:activity:7504539313766400028",
|
|
87
|
+
"activityId": "7504539313766400028"
|
|
88
|
+
},
|
|
89
|
+
"zoe": {
|
|
90
|
+
"id": "7504720507699200029",
|
|
91
|
+
"urn": "urn:li:share:7504720507699200029",
|
|
92
|
+
"activity": "urn:li:activity:7504720507699200030",
|
|
93
|
+
"activityId": "7504720507699200030"
|
|
94
|
+
},
|
|
95
|
+
"mayaArticle": {
|
|
96
|
+
"id": "7504901701632000031",
|
|
97
|
+
"urn": "urn:li:share:7504901701632000031",
|
|
98
|
+
"activity": "urn:li:activity:7504901701632000032",
|
|
99
|
+
"activityId": "7504901701632000032"
|
|
100
|
+
},
|
|
101
|
+
"northwindPriya": {
|
|
102
|
+
"id": "7505082895564800033",
|
|
103
|
+
"urn": "urn:li:share:7505082895564800033",
|
|
104
|
+
"activity": "urn:li:activity:7505082895564800034",
|
|
105
|
+
"activityId": "7505082895564800034"
|
|
106
|
+
},
|
|
107
|
+
"mayaPlain": {
|
|
108
|
+
"id": "7505264089497600035",
|
|
109
|
+
"urn": "urn:li:share:7505264089497600035",
|
|
110
|
+
"activity": "urn:li:activity:7505264089497600036",
|
|
111
|
+
"activityId": "7505264089497600036"
|
|
112
|
+
},
|
|
113
|
+
"mayaDraft": {
|
|
114
|
+
"id": "7505581178880000037",
|
|
115
|
+
"urn": "urn:li:share:7505581178880000037",
|
|
116
|
+
"activity": "urn:li:activity:7505581178880000038",
|
|
117
|
+
"activityId": "7505581178880000038"
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
export const COMMENTS = {
|
|
121
|
+
"samOnArticle": {
|
|
122
|
+
"id": "7504937940418560039",
|
|
123
|
+
"activityId": "7504901701632000032",
|
|
124
|
+
"urn": "urn:li:comment:(urn:li:activity:7504901701632000032,7504937940418560039)"
|
|
125
|
+
},
|
|
126
|
+
"priyaOnArticle": {
|
|
127
|
+
"id": "7504974179205120040",
|
|
128
|
+
"activityId": "7504901701632000032",
|
|
129
|
+
"urn": "urn:li:comment:(urn:li:activity:7504901701632000032,7504974179205120040)"
|
|
130
|
+
},
|
|
131
|
+
"mayaReplySam": {
|
|
132
|
+
"id": "7505010417991680041",
|
|
133
|
+
"activityId": "7504901701632000032",
|
|
134
|
+
"urn": "urn:li:comment:(urn:li:activity:7504901701632000032,7505010417991680041)"
|
|
135
|
+
},
|
|
136
|
+
"northwindReplyPriya": {
|
|
137
|
+
"id": "7505046656778240042",
|
|
138
|
+
"activityId": "7504901701632000032",
|
|
139
|
+
"urn": "urn:li:comment:(urn:li:activity:7504901701632000032,7505046656778240042)"
|
|
140
|
+
},
|
|
141
|
+
"zoeOnArticle": {
|
|
142
|
+
"id": "7505082895564800043",
|
|
143
|
+
"activityId": "7504901701632000032",
|
|
144
|
+
"urn": "urn:li:comment:(urn:li:activity:7504901701632000032,7505082895564800043)"
|
|
145
|
+
},
|
|
146
|
+
"danielOnReport": {
|
|
147
|
+
"id": "7503270956236800044",
|
|
148
|
+
"activityId": "7503089762304000018",
|
|
149
|
+
"urn": "urn:li:comment:(urn:li:activity:7503089762304000018,7503270956236800044)"
|
|
150
|
+
},
|
|
151
|
+
"aikoOnReport": {
|
|
152
|
+
"id": "7503452150169600045",
|
|
153
|
+
"activityId": "7503089762304000018",
|
|
154
|
+
"urn": "urn:li:comment:(urn:li:activity:7503089762304000018,7503452150169600045)"
|
|
155
|
+
},
|
|
156
|
+
"mayaOnSam": {
|
|
157
|
+
"id": "7496929168588800046",
|
|
158
|
+
"activityId": "7496566780723200004",
|
|
159
|
+
"urn": "urn:li:comment:(urn:li:activity:7496566780723200004,7496929168588800046)"
|
|
160
|
+
},
|
|
161
|
+
"noorOnSam": {
|
|
162
|
+
"id": "7497110362521600047",
|
|
163
|
+
"activityId": "7496566780723200004",
|
|
164
|
+
"urn": "urn:li:comment:(urn:li:activity:7496566780723200004,7497110362521600047)"
|
|
165
|
+
},
|
|
166
|
+
"graceOnBrightline": {
|
|
167
|
+
"id": "7502546180505600048",
|
|
168
|
+
"activityId": "7502364986572800014",
|
|
169
|
+
"urn": "urn:li:comment:(urn:li:activity:7502364986572800014,7502546180505600048)"
|
|
170
|
+
},
|
|
171
|
+
"mayaOnZoe": {
|
|
172
|
+
"id": "7504756746485760049",
|
|
173
|
+
"activityId": "7504720507699200030",
|
|
174
|
+
"urn": "urn:li:comment:(urn:li:activity:7504720507699200030,7504756746485760049)"
|
|
175
|
+
},
|
|
176
|
+
"elenaOnZoe": {
|
|
177
|
+
"id": "7504829224058880050",
|
|
178
|
+
"activityId": "7504720507699200030",
|
|
179
|
+
"urn": "urn:li:comment:(urn:li:activity:7504720507699200030,7504829224058880050)"
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
export const COUNTS = {"posts":19,"comments":12,"reactions":14};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Comment flows: create, reply, list, get, edit and delete, with validation and ownership errors.
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { del, enc, get, op, orgUrn, partial, personUrn, post } from "../client.mjs";
|
|
4
|
+
import { COMMENTS, ORGS, PEOPLE, POSTS } from "../fixtures.mjs";
|
|
5
|
+
|
|
6
|
+
const maya = personUrn(PEOPLE.maya);
|
|
7
|
+
const base = (target) => `/rest/socialActions/${enc(target)}/comments`;
|
|
8
|
+
const art = POSTS.mayaArticle;
|
|
9
|
+
|
|
10
|
+
async function create() {
|
|
11
|
+
const text = "Thanks Zoë Łukasiewicz for the review";
|
|
12
|
+
const mention = { start: text.indexOf("Zoë"), length: 15, value: { person: { person: personUrn(PEOPLE.zoe) } } };
|
|
13
|
+
const top = await post(base(art.urn), { actor: maya, object: art.activity, message: { text, attributes: [mention] } });
|
|
14
|
+
assert.equal(top.headers.get("x-restli-id"), top.body.id);
|
|
15
|
+
assert.equal(top.body.commentUrn, `urn:li:comment:(${art.activity},${top.body.id})`);
|
|
16
|
+
assert.deepEqual(top.body.message.attributes, [mention]);
|
|
17
|
+
assert.equal(top.body.object, art.activity);
|
|
18
|
+
const reply = await post(base(COMMENTS.samOnArticle.urn), { actor: maya, object: art.activity, message: { text: "Replying again" } });
|
|
19
|
+
assert.equal(reply.body.parentComment, COMMENTS.samOnArticle.urn);
|
|
20
|
+
assert.equal(reply.body.commentsSummary, undefined);
|
|
21
|
+
await post(base(art.urn), { actor: maya, message: { text: "A reply by field" }, parentComment: COMMENTS.priyaOnArticle.urn });
|
|
22
|
+
const page = await post(base(POSTS.northwindArticle.urn), { actor: orgUrn(ORGS.northwind), message: { text: "Thanks for reading" } });
|
|
23
|
+
assert.equal(page.body.agent, maya);
|
|
24
|
+
assert.equal(page.body.created.impersonator, maya);
|
|
25
|
+
const bad = (target, body, expect) => post(base(target), body, expect);
|
|
26
|
+
const msg = (text) => ({ actor: maya, message: { text } });
|
|
27
|
+
await bad(COMMENTS.mayaReplySam.urn, msg("reply to a reply"), [422, "UNPROCESSABLE_ENTITY"]);
|
|
28
|
+
await bad(art.urn, { ...msg("x"), message: { text: "x", attributes: [{ start: 0, length: 5, value: { person: { person: maya } } }] } },
|
|
29
|
+
[400, "INVALID_VALUE_FOR_FIELD"]);
|
|
30
|
+
await bad(art.urn, { ...msg("x"), object: POSTS.sam.activity }, [400, "INVALID_VALUE_FOR_FIELD"]);
|
|
31
|
+
await bad(art.urn, msg("c".repeat(1251)), [400, "FIELD_LENGTH_TOO_LONG"]);
|
|
32
|
+
await bad(art.urn, msg(" "), [400, "INVALID_VALUE_BLANK_FIELD"]);
|
|
33
|
+
await bad(art.urn, { actor: maya }, [400, "MISSING_FIELD"]);
|
|
34
|
+
await bad(art.urn, { message: { text: "x" } }, [400, "MISSING_FIELD"]);
|
|
35
|
+
await bad(art.urn, { ...msg("x"), content: [{ entity: { image: "urn:li:image:1" } }] }, [403, "ACCESS_DENIED"]);
|
|
36
|
+
await bad(POSTS.mayaClosed.urn, msg("closed"), [422, "UNPROCESSABLE_ENTITY"]);
|
|
37
|
+
await bad(POSTS.mayaDraft.urn, msg("draft"), [422, "UNPROCESSABLE_ENTITY"]);
|
|
38
|
+
await bad(art.urn, { actor: personUrn(PEOPLE.sam), message: { text: "x" } }, [403, "ACCESS_DENIED"]);
|
|
39
|
+
await bad("urn:li:share:12345", msg("x"), [404, "NOT_FOUND"]);
|
|
40
|
+
await bad(art.urn, { ...msg("x"), parentComment: `urn:li:comment:(${art.activity},12345)` }, [404, "NOT_FOUND"]);
|
|
41
|
+
await bad(POSTS.jordan.urn, msg("hidden"), [403, "ACCESS_DENIED"]);
|
|
42
|
+
await bad("urn:li:share:x", msg("x"), [400, "BAD_REQUEST"]);
|
|
43
|
+
await op("comments.create", { target: maya, ...msg("x") }, "INVALID_URN_TYPE");
|
|
44
|
+
await op("comments.create", { target: "urn:li:share:", ...msg("x") }, "INVALID_URN_ID");
|
|
45
|
+
return { top: top.body, page: page.body };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function listAndGet() {
|
|
49
|
+
const first = (await get(`${base(art.urn)}?count=2`)).body;
|
|
50
|
+
assert.equal(first.paging.total, 4);
|
|
51
|
+
assert.deepEqual(first.elements.map((c) => c.id), [COMMENTS.samOnArticle.id, COMMENTS.priyaOnArticle.id]);
|
|
52
|
+
assert.equal(first.elements[0].commentsSummary.totalFirstLevelComments, 2);
|
|
53
|
+
assert.equal(first.elements[0].likesSummary.likedByCurrentUser, true);
|
|
54
|
+
assert.equal(first.paging.links[0].rel, "next");
|
|
55
|
+
const second = (await get(`${base(art.urn)}?count=2&start=2`)).body;
|
|
56
|
+
assert.equal(second.elements.length, 2);
|
|
57
|
+
assert.equal(second.elements[0].id, COMMENTS.zoeOnArticle.id);
|
|
58
|
+
assert.equal((await get(base(COMMENTS.samOnArticle.urn))).body.paging.total, 2);
|
|
59
|
+
assert.deepEqual((await get(base(POSTS.mayaPlain.urn))).body.elements, []);
|
|
60
|
+
assert.equal((await get(base(art.activity))).body.paging.total, 4);
|
|
61
|
+
await get(`${base(art.urn)}?count=101`, [400, "INVALID_VALUE_FOR_FIELD"]);
|
|
62
|
+
await get(base("urn:li:share:12345"), [404, "NOT_FOUND"]);
|
|
63
|
+
await get(base(POSTS.jordan.urn), [403, "ACCESS_DENIED"]);
|
|
64
|
+
await op("comments.list", { target: maya }, "INVALID_URN_TYPE");
|
|
65
|
+
await op("comments.list", { target: "urn:li:activity:x" }, "INVALID_URN_ID");
|
|
66
|
+
const zoe = (await get(`${base(art.urn)}/${COMMENTS.zoeOnArticle.id}`)).body;
|
|
67
|
+
assert.deepEqual(zoe.message.attributes, [{ start: 16, length: 11, value: { person: { person: maya } } }]);
|
|
68
|
+
await get(`${base(POSTS.sam.urn)}/${COMMENTS.zoeOnArticle.id}`, [404, "NOT_FOUND"]);
|
|
69
|
+
await get(`${base(POSTS.jordan.urn)}/1`, [403, "ACCESS_DENIED"]);
|
|
70
|
+
await op("comments.get", { target: art.urn, commentId: "abc" }, "INVALID_URN_ID");
|
|
71
|
+
await op("comments.get", { target: maya, commentId: "1" }, "INVALID_URN_TYPE");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function editAndDelete({ top, page }) {
|
|
75
|
+
const edited = await partial(`${base(art.urn)}/${top.id}`, { patch: { message: { $set: { text: "Edited thanks" } } } }, 200);
|
|
76
|
+
assert.equal(edited.body.message.text, "Edited thanks");
|
|
77
|
+
assert.deepEqual(edited.body.message.attributes, []);
|
|
78
|
+
assert.equal(edited.headers.get("x-resourceidentity-urn"), top.commentUrn);
|
|
79
|
+
const url = `${base(POSTS.northwindArticle.urn)}/${page.id}?actor=${enc(orgUrn(ORGS.northwind))}`;
|
|
80
|
+
assert.equal((await partial(url, { patch: { message: { $set: { text: "Thanks for reading!" } } } }, 200)).body.actor, orgUrn(ORGS.northwind));
|
|
81
|
+
const edit = (id, body, expect) => partial(`${base(art.urn)}/${id}`, body, expect);
|
|
82
|
+
const set = (text) => ({ patch: { message: { $set: { text } } } });
|
|
83
|
+
await edit(COMMENTS.priyaOnArticle.id, set("not mine"), [403, "ACCESS_DENIED"]);
|
|
84
|
+
await edit("12345", set("missing"), [404, "NOT_FOUND"]);
|
|
85
|
+
await edit(top.id, set(" "), [400, "INVALID_VALUE_BLANK_FIELD"]);
|
|
86
|
+
await edit(top.id, set("e".repeat(1251)), [400, "FIELD_LENGTH_TOO_LONG"]);
|
|
87
|
+
await edit(top.id, { patch: { foo: 1 } }, [400, "INVALID_VALUE_FOR_FIELD"]);
|
|
88
|
+
await op("comments.update", { target: art.urn, commentId: top.id }, "MISSING_FIELD");
|
|
89
|
+
await op("comments.update", { target: maya, commentId: top.id, message: { text: "x" } }, "INVALID_URN_TYPE");
|
|
90
|
+
await op("comments.update", { target: art.urn, commentId: "x1", message: { text: "x" } }, "INVALID_URN_ID");
|
|
91
|
+
|
|
92
|
+
const removed = await op("comments.delete", { target: art.urn, commentId: COMMENTS.samOnArticle.id });
|
|
93
|
+
assert.deepEqual(removed.value, { commentUrn: COMMENTS.samOnArticle.urn, deleted: true, deletedReplies: 2 });
|
|
94
|
+
await del(`${base(art.urn)}/${top.id}`);
|
|
95
|
+
await get(`${base(art.urn)}/${top.id}`, [404, "NOT_FOUND"]);
|
|
96
|
+
await del(`${base(POSTS.sam.urn)}/${COMMENTS.noorOnSam.id}`, [403, "ACCESS_DENIED"]);
|
|
97
|
+
await del(`${base(art.urn)}/12345`, [404, "NOT_FOUND"]);
|
|
98
|
+
await op("comments.delete", { target: maya, commentId: "1" }, "INVALID_URN_TYPE");
|
|
99
|
+
await op("comments.delete", { target: art.urn, commentId: "" }, "INVALID_URN_ID");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function comments() {
|
|
103
|
+
const created = await create();
|
|
104
|
+
await listAndGet();
|
|
105
|
+
await editAndDelete(created);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function commentsConnection() {
|
|
109
|
+
await del(`${base(POSTS.sam.urn)}/${COMMENTS.mayaOnSam.id}`);
|
|
110
|
+
await partial(`${base(POSTS.sam.urn)}/${COMMENTS.noorOnSam.id}`, { patch: { message: { $set: { text: "edited by Sam" } } } }, [403, "ACCESS_DENIED"]);
|
|
111
|
+
await del(`${base(POSTS.brightline.urn)}/${COMMENTS.graceOnBrightline.id}?actor=${enc(orgUrn(ORGS.brightline))}`);
|
|
112
|
+
await partial(`${base(POSTS.zoe.urn)}/${COMMENTS.mayaOnZoe.id}`, { patch: { message: { $set: { text: "x" } } } }, [403, "ACCESS_DENIED"]);
|
|
113
|
+
assert.equal((await get(base(POSTS.sam.urn))).body.paging.total, 1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const FLOWS = { comments, "comments-connection": commentsConnection };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Calls every operation once through the canonical endpoint with arguments that are valid for Maya's seeded network.
|
|
2
|
+
import { op, orgUrn, personUrn } from "../client.mjs";
|
|
3
|
+
import { COMMENTS, ORGS, PEOPLE, POSTS } from "../fixtures.mjs";
|
|
4
|
+
|
|
5
|
+
export function everyCall() {
|
|
6
|
+
const maya = personUrn(PEOPLE.maya);
|
|
7
|
+
return [
|
|
8
|
+
["profile.userinfo", {}],
|
|
9
|
+
["profile.me", {}],
|
|
10
|
+
["people.get", { personId: PEOPLE.sam }],
|
|
11
|
+
["posts.create", { author: maya, commentary: "probe", visibility: "PUBLIC", distribution: { feedDistribution: "MAIN_FEED" }, lifecycleState: "PUBLISHED" }],
|
|
12
|
+
["posts.get", { postUrn: POSTS.mayaPlain.urn }],
|
|
13
|
+
["posts.list_by_author", { author: maya }],
|
|
14
|
+
["posts.update", { postUrn: POSTS.mayaPlain.urn, commentary: "probe edit" }],
|
|
15
|
+
["posts.delete", { postUrn: POSTS.mayaPlain.urn }],
|
|
16
|
+
["feed.list", {}],
|
|
17
|
+
["comments.create", { target: POSTS.mayaPlain.urn, actor: maya, message: { text: "probe" } }],
|
|
18
|
+
["comments.list", { target: POSTS.mayaArticle.urn }],
|
|
19
|
+
["comments.get", { target: POSTS.mayaArticle.urn, commentId: COMMENTS.samOnArticle.id }],
|
|
20
|
+
["comments.update", { target: POSTS.zoe.urn, commentId: COMMENTS.mayaOnZoe.id, message: { text: "probe" } }],
|
|
21
|
+
["comments.delete", { target: POSTS.zoe.urn, commentId: COMMENTS.mayaOnZoe.id }],
|
|
22
|
+
["reactions.create", { actor: maya, root: POSTS.mayaPlain.urn, reactionType: "LIKE" }],
|
|
23
|
+
["reactions.list", { entity: POSTS.mayaArticle.activity }],
|
|
24
|
+
["reactions.delete", { actor: maya, entity: POSTS.zoe.activity }],
|
|
25
|
+
["social_metadata.get", { entity: POSTS.mayaArticle.urn }],
|
|
26
|
+
["social_metadata.set_comments_state", { entity: POSTS.mayaPlain.urn, actor: maya, commentsState: "CLOSED" }],
|
|
27
|
+
["organizations.get", { organizationId: String(ORGS.northwind) }],
|
|
28
|
+
["organizations.find_by_vanity_name", { vanityName: "northwind-analytics" }],
|
|
29
|
+
["organization_acls.list", { q: "roleAssignee" }],
|
|
30
|
+
["network_sizes.get", { entity: orgUrn(ORGS.northwind), edgeType: "COMPANY_FOLLOWED_BY_MEMBER" }],
|
|
31
|
+
["connections.list", {}],
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Every operation must answer `expect` ("denied" or a declared error code). */
|
|
36
|
+
export async function everyOperation(expect) {
|
|
37
|
+
for (const [operationId, args] of everyCall()) await op(operationId, args, expect);
|
|
38
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Fault scenarios and the scan-bound scenario.
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { del, enc, get, op, partial, personUrn, post, textPost } from "../client.mjs";
|
|
4
|
+
import { COMMENTS, PEOPLE, POSTS } from "../fixtures.mjs";
|
|
5
|
+
|
|
6
|
+
const maya = personUrn(PEOPLE.maya);
|
|
7
|
+
const social = (target) => `/rest/socialActions/${enc(target)}/comments`;
|
|
8
|
+
const reactionKey = (entity) => `/rest/reactions/${enc(`(actor:${maya},entity:${entity})`)}`;
|
|
9
|
+
|
|
10
|
+
async function commentThrottled() {
|
|
11
|
+
const refused = (await post(social(POSTS.mayaPlain.urn), { actor: maya, message: { text: "First!" } }, [429, "TOO_MANY_REQUESTS"])).body;
|
|
12
|
+
assert.match(refused.message, /Comment create throttled/);
|
|
13
|
+
assert.equal((await get(social(POSTS.mayaPlain.urn))).body.paging.total, 0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function postLostResponse() {
|
|
17
|
+
const find = `/rest/posts?q=author&author=${enc(maya)}&count=100`;
|
|
18
|
+
assert.equal((await get(find)).body.paging.total, 11);
|
|
19
|
+
await post("/rest/posts", textPost(maya, "Quarterly roadmap is live"), [500, "INTERNAL_SERVER_ERROR"]);
|
|
20
|
+
const after = (await get(find)).body;
|
|
21
|
+
assert.equal(after.paging.total, 12, "the post was committed even though the caller saw 500");
|
|
22
|
+
assert.ok(after.elements.some((p) => p.commentary === "Quarterly roadmap is live"));
|
|
23
|
+
await post("/rest/posts", textPost(maya, "Quarterly roadmap is live"), [500, "INTERNAL_SERVER_ERROR"]);
|
|
24
|
+
const duplicates = (await get(find)).body.elements.filter((p) => p.commentary === "Quarterly roadmap is live");
|
|
25
|
+
assert.equal(duplicates.length, 2, "a blind retry publishes a duplicate");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function reactionsOutage() {
|
|
29
|
+
await post(`/rest/reactions?actor=${enc(maya)}`, { root: POSTS.mayaPlain.urn, reactionType: "LIKE" }, [503, "SERVICE_UNAVAILABLE"]);
|
|
30
|
+
await del(reactionKey(POSTS.zoe.activity), [503, "SERVICE_UNAVAILABLE"]);
|
|
31
|
+
assert.equal((await get(reactionKey(POSTS.zoe.activity))).body.reactionType, "EMPATHY");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function tightLimits() {
|
|
35
|
+
const bound = [500, "INTERNAL_SERVER_ERROR"];
|
|
36
|
+
const checkBound = (response) => assert.equal(response.body.message, "state exceeds the supported bound of 1 rows");
|
|
37
|
+
checkBound(await get(`/rest/posts?q=author&author=${enc(maya)}`, bound));
|
|
38
|
+
await del(`/rest/posts/${enc(POSTS.mayaArticle.urn)}`, bound);
|
|
39
|
+
await op("feed.list", {}, "INTERNAL_SERVER_ERROR");
|
|
40
|
+
await get(social(POSTS.mayaArticle.urn), bound);
|
|
41
|
+
await get(`${social(POSTS.mayaArticle.urn)}/${COMMENTS.samOnArticle.id}`, bound);
|
|
42
|
+
await partial(`${social(POSTS.zoe.urn)}/${COMMENTS.mayaOnZoe.id}`, { patch: { message: { $set: { text: "edited" } } } }, bound);
|
|
43
|
+
await del(`${social(POSTS.zoe.urn)}/${COMMENTS.mayaOnZoe.id}`, bound);
|
|
44
|
+
await get(`/rest/reactions/${enc(`(entity:${POSTS.mayaArticle.activity})`)}?q=entity`, bound);
|
|
45
|
+
await get(`/rest/socialMetadata/${enc(POSTS.mayaArticle.urn)}`, bound);
|
|
46
|
+
await post(`/rest/socialMetadata/${enc(POSTS.mayaArticle.urn)}?actor=${enc(maya)}`, { patch: { $set: { commentsState: "CLOSED" } } }, bound);
|
|
47
|
+
await get("/rest/organizationAcls?q=roleAssignee", bound);
|
|
48
|
+
await get(`/v2/connections/${enc(maya)}`, 500);
|
|
49
|
+
await op("connections.list", {}, "INTERNAL_SERVER_ERROR");
|
|
50
|
+
assert.equal((await get(`/rest/posts/${enc(POSTS.mayaArticle.urn)}`)).body.id, POSTS.mayaArticle.urn);
|
|
51
|
+
assert.equal((await get(`${social(POSTS.zoe.urn)}/${COMMENTS.elenaOnZoe.id}`, bound)).status, 500);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const FLOWS = {
|
|
55
|
+
"comment-throttled": commentThrottled, "post-lost-response": postLostResponse, "reactions-outage": reactionsOutage, "tight-limits": tightLimits,
|
|
56
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Identity, token and grant flows.
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { enc, get, op, personUrn } from "../client.mjs";
|
|
4
|
+
import { COMMENTS, PEOPLE, POSTS } from "../fixtures.mjs";
|
|
5
|
+
import { everyOperation } from "./every.mjs";
|
|
6
|
+
|
|
7
|
+
async function identity() {
|
|
8
|
+
const info = (await get("/v2/userinfo")).body;
|
|
9
|
+
assert.deepEqual(info, {
|
|
10
|
+
sub: PEOPLE.maya, name: "Maya Okafor", given_name: "Maya", family_name: "Okafor", picture: `https://media.example.com/profiles/${PEOPLE.maya}.jpg`,
|
|
11
|
+
locale: "en-US", email: "maya.okafor@example.com", email_verified: true,
|
|
12
|
+
});
|
|
13
|
+
const me = (await get("/v2/me")).body;
|
|
14
|
+
assert.equal(me.id, PEOPLE.maya);
|
|
15
|
+
assert.equal(me.localizedHeadline, "Head of Data Platform at Northwind Analytics");
|
|
16
|
+
assert.deepEqual(me.firstName, { localized: { en_US: "Maya" }, preferredLocale: { country: "US", language: "en" } });
|
|
17
|
+
const zoe = (await get(`/v2/people/${enc(`(id:${PEOPLE.zoe})`)}`)).body;
|
|
18
|
+
assert.equal(zoe.localizedLastName, "Łukasiewicz");
|
|
19
|
+
assert.equal(zoe.localizedHeadline, undefined);
|
|
20
|
+
assert.deepEqual(Object.keys(zoe.firstName.localized), ["pl_PL"]);
|
|
21
|
+
await get(`/v2/people/${enc(`(name:${PEOPLE.zoe})`)}`, 400);
|
|
22
|
+
await get(`/v2/people/${enc("(id:zzzzzzzzzz)")}`, 404);
|
|
23
|
+
await op("people.get", { personId: "bad id" }, "BAD_REQUEST");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function freshInstall() {
|
|
27
|
+
const feed = (await op("feed.list", {})).value;
|
|
28
|
+
assert.equal(feed.viewer.personUrn, personUrn(PEOPLE.maya));
|
|
29
|
+
assert.equal(feed.serverTime, "2026-09-15T14:00:00.000Z");
|
|
30
|
+
assert.ok(feed.elements.some((item) => item.post.id === POSTS.mayaArticle.urn));
|
|
31
|
+
assert.equal((await op("profile.me", {})).value.id, PEOPLE.maya);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function invalidToken() {
|
|
35
|
+
await everyOperation("INVALID_ACCESS_TOKEN");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function denied() {
|
|
39
|
+
await everyOperation("denied");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function openidOnly() {
|
|
43
|
+
await get("/v2/userinfo", 403);
|
|
44
|
+
await get("/v2/me", 403);
|
|
45
|
+
await everyOperation("ACCESS_DENIED");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function limitedScopes() {
|
|
49
|
+
const info = (await get("/v2/userinfo")).body;
|
|
50
|
+
assert.equal(info.email, undefined);
|
|
51
|
+
assert.equal(info.email_verified, undefined);
|
|
52
|
+
await get(`/rest/posts?q=author&author=${enc(personUrn(PEOPLE.maya))}`, [403, "ACCESS_DENIED"]);
|
|
53
|
+
await op("feed.list", {}, "ACCESS_DENIED");
|
|
54
|
+
await op("connections.list", {}, "ACCESS_DENIED");
|
|
55
|
+
const created = await op("posts.create", {
|
|
56
|
+
author: personUrn(PEOPLE.maya), commentary: "Posting works with w_member_social alone.", visibility: "PUBLIC",
|
|
57
|
+
distribution: { feedDistribution: "MAIN_FEED" }, lifecycleState: "PUBLISHED",
|
|
58
|
+
});
|
|
59
|
+
assert.match(created.value.id, /^urn:li:share:[0-9]+$/);
|
|
60
|
+
await op("posts.create", {
|
|
61
|
+
author: "urn:li:organization:81234567", commentary: "Needs w_organization_social.", visibility: "PUBLIC",
|
|
62
|
+
distribution: { feedDistribution: "MAIN_FEED" }, lifecycleState: "PUBLISHED",
|
|
63
|
+
}, "ACCESS_DENIED");
|
|
64
|
+
await op("comments.list", { target: POSTS.mayaArticle.urn }, "ACCESS_DENIED");
|
|
65
|
+
void COMMENTS;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const FLOWS = {
|
|
69
|
+
identity, "fresh-install": freshInstall, "invalid-token": invalidToken, denied, "openid-only": openidOnly, "limited-scopes": limitedScopes,
|
|
70
|
+
};
|