Skip to content

Ci desktop regression self hosted (WIP) #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 149 additions & 4 deletions .github/workflows/desktop-regression.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run android regression tests
name: Run desktop regression tests
on:
workflow_dispatch:
inputs:
Expand All @@ -7,15 +7,160 @@ on:
required: true
type: choice
options:
- oxen-io/session-playwright
- session-foundation/session-playwright
- burtonemily/session-playwright
- bilb/session-playwright
default: oxen-io/session-playwright
default: session-foundation/session-playwright

BRANCH_TO_CHECKOUT_PW:
description: 'branch to checkout on session-playwright'
required: true
type: string
default: ci-desktop-regression-self-hosted

SESSION_DESKTOP_REPO:
description: 'Session desktop repo to checkout'
required: true
type: choice
options:
- session-foundation/session-desktop
- bilb/session-desktop
default: session-foundation/session-desktop

BRANCH_TO_CHECKOUT_SESSION:
description: 'Branch to checkout on session-desktop'
required: true
type: string
default: unstable

PLAYWRIGHT_REPEAT_COUNT:
description: 'Repeats of each tests (0 to only run each once)'
required: true
type: number
default: 0

PLAYWRIGHT_RETRIES_COUNT:
description: 'Retries of each tests (0 to only run each once, 1 to run another attempt)'
required: true
type: number
default: 0

PLAYWRIGHT_WORKER_COUNT:
description: 'Playwright workers to start'
required: true
type: number
default: 6

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true

jobs:
desktop-regression:
name: Desktop Regression Tests
runs-on: [self-hosted, linux, x64, qa-desktop]
runs-on: [self-hosted, Linux, X64, qa-desktop]
container:
image: mcr.microsoft.com/playwright:v1.47.1-noble
options: --cpus 16

env:
PLAYWRIGHT_CUSTOM_REPORTER: 1
PLAYWRIGHT_REPEAT_COUNT: ${{ github.event.inputs.PLAYWRIGHT_REPEAT_COUNT }}
PLAYWRIGHT_RETRIES_COUNT: ${{ github.event.inputs.PLAYWRIGHT_RETRIES_COUNT }}
PLAYWRIGHT_WORKER_COUNT: ${{ github.event.inputs.PLAYWRIGHT_WORKER_COUNT }}
DESKTOP_CACHED_FOLDER: desktop/node_modules

steps:
- uses: actions/checkout@v4
- name: Runner Details
run: |
echo "PLAYWRIGHT_REPO ${{ github.event.inputs.PLAYWRIGHT_REPO }}"
echo "BRANCH_TO_CHECKOUT_PW ${{ github.event.inputs.BRANCH_TO_CHECKOUT_PW }}"
echo "SESSION_DESKTOP_REPO ${{ github.event.inputs.SESSION_DESKTOP_REPO }}"
echo "BRANCH_TO_CHECKOUT_SESSION ${{ github.event.inputs.BRANCH_TO_CHECKOUT_SESSION }}"

- uses: actions/checkout@v4
name: 'Checkout playwright'
with:
repository: ${{ github.event.inputs.PLAYWRIGHT_REPO }}
ref: ${{ github.event.inputs.BRANCH_TO_CHECKOUT_PW }}
path: 'playwright'

- name: Install system deps
run: apt update && apt install -y git g++ build-essential cmake

- uses: actions/checkout@v4
name: 'Checkout Session desktop'
with:
repository: ${{ github.event.inputs.SESSION_DESKTOP_REPO }}
ref: ${{ github.event.inputs.BRANCH_TO_CHECKOUT_SESSION }}
path: 'desktop'

- name: Calculate desktop cache key
run: |
echo "CACHE_KEY=${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('desktop/package.json', 'desktop/yarn.lock', 'desktop/patches/**') }}" >> $GITHUB_ENV

- name: Install node
uses: actions/setup-node@v3
with:
node-version-file: 'desktop/.nvmrc'

- uses: actions/setup-python@v4
with:
python-version: '3.12'

- uses: actions/cache/restore@v4
id: cache-desktop-modules
with:
path: ${{ env.DESKTOP_CACHED_FOLDER }}
key: ${{ env.CACHE_KEY }}

- name: Install yarn
run: |
npm install -g yarn

- name: List desktop folder
run: |
pwd
ls -la desktop

- name: List playwright folder
run: |
pwd
ls -la playwright

- name: Install desktop dependencies
shell: bash
if: steps.cache-desktop-modules.outputs.cache-hit != 'true'
run: cd $GITHUB_WORKSPACE/desktop && yarn install --frozen-lockfile --network-timeout 600000

- uses: actions/cache/save@v4
if: always()
with:
path: ${{ env.DESKTOP_CACHED_FOLDER }}
key: ${{ env.CACHE_KEY }}

- name: Build desktop
shell: bash
run: cd $GITHUB_WORKSPACE/desktop && yarn build-everything

- name: Install playwright dependencies
run: |
cd $GITHUB_WORKSPACE/playwright && yarn install --frozen-lockfile

- name: Build the Desktop tests
run: |
cd $GITHUB_WORKSPACE/playwright
yarn tsc

- name: Run the Desktop tests
run: |
cd $GITHUB_WORKSPACE/playwright
SESSION_DESKTOP_ROOT=$GITHUB_WORKSPACE/desktop nice ionice xvfb-run --auto-servernum --server-num=1 --server-args='-screen 0, 1920x1080x24' yarn test

- name: Kill all running electron app
if: always()
continue-on-error: true # just so we don't fail
shell: bash
run: |
killall electron;
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This repository holds the code to do regression testing with Playwright for Sess
## Setup


`git clone https://github.com/oxen-io/session-playwright/`
`git clone https://github.com/session-foundation/session-playwright/`

Install [nvm](https://github.com/nvm-sh/nvm) or [nvm for windows](https://github.com/coreybutler/nvm-windows).

Expand Down
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
"name": "session-playwright",
"version": "1.0.0",
"main": "index.js",
"repository": "[email protected]:oxen-io/session-playwright.git",
"repository": "[email protected]:session-foundation/session-playwright.git",
"author": "Audric Ackermann <[email protected]>",
"license": "MIT",
"devDependencies": {
"@playwright/test": "^1.37.1",
"@types/fs-extra": "^11.0.1",
"@types/libsodium-wrappers-sumo": "^0.7.8",
"@types/lodash": "^4.14.196",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^6.2.1",
Expand All @@ -26,7 +27,8 @@
"fs-extra": "^11.1.1",
"lodash": "^4.17.21",
"prettier": "^3.0.1",
"typescript": "^5.1.6",
"semver": "^7.5.4",
"typescript": "^5.5.4",
"uuid": "^9.0.1"
},
"scripts": {
Expand All @@ -37,5 +39,9 @@
"allure-create": "allure generate ./allure-results",
"allure-open": "allure open ./allure-report"
},
"dependencies": {}
"dependencies": {
"buffer-crc32": "^1.0.0",
"libsodium-wrappers-sumo": "^0.7.14",
"session-tooling": "git+https://github.com/Bi1b/session-tooling"
}
}
41 changes: 41 additions & 0 deletions state_generation/actions/fetchSwarmOf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { isEmpty, sample } from 'lodash';
import { SwarmForSubRequest } from '../requests/snodeRequests';
import { PubkeyType, Snode, SnodeFromSeed } from '../requests/types';

const fetchedSwarms: Record<PubkeyType, Array<Snode>> = {};

async function getSwarmOfUser(sessionId: PubkeyType, snode: SnodeFromSeed) {
const swarmRequest = new SwarmForSubRequest(sessionId);

const swarmResult = await fetch(
`https://${snode.public_ip}:${snode.storage_port}/storage_rpc/v1`,
{
body: JSON.stringify(await swarmRequest.build()),
method: 'POST',
},
);
const swarm = await swarmResult.json();

if (isEmpty(fetchedSwarms[sessionId])) {
fetchedSwarms[sessionId] = swarm;
}
// console.error('fetched userSwarm', swarm.snodes);

return swarm.snodes;
}

export async function randomSnodeOnUserSwarm(
sessionId: PubkeyType,
snode: SnodeFromSeed,
) {
const userSwarm =
fetchedSwarms[sessionId] || (await getSwarmOfUser(sessionId, snode));
const randomSnodeOnSwarm = sample(userSwarm);
if (!randomSnodeOnSwarm) {
throw new Error(`did not find a snode for user: ${sessionId}`);
}
console.info(
`random snode for user: ${sessionId} is snode: ${randomSnodeOnSwarm.pubkey_ed25519}`,
);
return randomSnodeOnSwarm;
}
131 changes: 131 additions & 0 deletions state_generation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { sample } from 'lodash';
import { getSodiumNode, WithSodium } from '../tests/sodium';
import { randomSnodeOnUserSwarm } from './actions/fetchSwarmOf';

import { getAllSnodesFromSeed } from './requests/seedRequest';
import { PubkeyType } from './requests/types';
import { loadSessionTools, WithSessionTools } from './sessionTools';
import { createRandomUser, SessionUser } from './sessionUser';

function createRandomLegacyGroup({ sodium }: WithSodium) {
const seed = sodium.randombytes_buf(sodium.crypto_sign_ed25519_SEEDBYTES);
const ed25519KeyPair = sodium.crypto_sign_seed_keypair(seed);
const privkeyHex = sodium.to_hex(ed25519KeyPair.privateKey);

// 64 privkey + 64 pubkey
const publicKey = sodium.crypto_sign_ed25519_sk_to_pk(
sodium.from_hex(privkeyHex),
);
const x25519Pk = sodium.crypto_sign_ed25519_pk_to_curve25519(publicKey);

const encKeypair = sodium.crypto_sign_keypair();
const groupPubkey = new Uint8Array(33);
groupPubkey.set(x25519Pk, 1);
groupPubkey[0] = 5;

const groupPk = sodium.to_hex(groupPubkey) as PubkeyType;
return {
groupPk,
encPk: encKeypair.publicKey,
encSk: encKeypair.privateKey,
};
}
// type CreatedLegacyGroup = ReturnType<typeof createRandomLegacyGroup>;

function makeFriendsAndKnown(...users: Array<SessionUser>) {
if (users.length < 2) {
throw new Error('needs at least two users to make them friends');
}
users.forEach((user1, index) => {
const user2 = users[index + 1];
if (user2) {
user1.contacts.setApproved(user2.sessionId, true);
user1.contacts.setApprovedMe(user2.sessionId, true);
user2.contacts.setApproved(user1.sessionId, true);
user2.contacts.setApprovedMe(user1.sessionId, true);
user2.contacts.setName(
user1.sessionId,
user1.userProfile.getName() || '',
);
user1.contacts.setName(
user2.sessionId,
user2.userProfile.getName() || '',
);
}
});
}

function makeGroupWithMembers({
members,
groupName,
sodium,
}: {
members: Array<SessionUser>;
groupName: string;
} & WithSodium) {
// first one is the creator
if (!members.length) {
throw new Error('Excepted at least one creator/member');
}
const [creator, ...otherMembers] = members;
const { encPk, encSk, groupPk } = createRandomLegacyGroup({ sodium });
const legacyGroup = creator.userGroups.getOrConstructLegacyGroup(groupPk);
legacyGroup.name = groupName;
legacyGroup.encPubkey = encPk;
legacyGroup.encSeckey = encSk;
legacyGroup.insert(creator.sessionId, true);
otherMembers.forEach((member) => {
legacyGroup.insert(member.sessionId, false); // only one admin for legacy groups
});

[creator, ...otherMembers].forEach((member) => {
member.userGroups.setLegacyGroup(legacyGroup);
});
}

export function twoUsersFriends(opts: WithSodium & WithSessionTools) {
const alice = createRandomUser(opts);
const bob = createRandomUser(opts);
makeFriendsAndKnown(alice, bob);
}

export async function prepareThreeFriendsInSharedGroup() {
const sodium = await getSodiumNode();
const sessionTools = await loadSessionTools();

const alice = createRandomUser({ sodium, sessionTools });
const bob = createRandomUser({ sodium, sessionTools });
const charlie = createRandomUser({ sodium, sessionTools });
const users = [alice, bob, charlie];

try {
alice.userProfile.setName('Alice');
bob.userProfile.setName('Bob');
charlie.userProfile.setName('Charlie');

makeFriendsAndKnown(alice, bob, charlie);

makeGroupWithMembers({
groupName: 'group test 1',
members: [alice, bob, charlie],
sodium,
});

const snodesInNetwork = await getAllSnodesFromSeed();

const randomSnodeFromSeed = sample(snodesInNetwork);
await Promise.all(
users.map(async (user) => {
const randomSnodeOnSwarm = await randomSnodeOnUserSwarm(
user.sessionId,
randomSnodeFromSeed,
);
await user.pushChangesToSwarm(randomSnodeOnSwarm);
}),
);
console.warn(`seed of alice: "${users[0].seedPhrase}"`);
return users.map((m) => ({ seed: m.seed, sessionId: m.sessionId }));
} finally {
users.map((user) => user.freeMemory());
}
}
Loading
Loading