katetaan puuttuva testit
This commit is contained in:
@@ -8,9 +8,17 @@ REPORT_DIR="${2:-}"
|
|||||||
[ -n "$REPORT_DIR" ] || { echo "ERROR: report directory required" >&2; exit 1; }
|
[ -n "$REPORT_DIR" ] || { echo "ERROR: report directory required" >&2; exit 1; }
|
||||||
|
|
||||||
HAS_COVERAGE=false
|
HAS_COVERAGE=false
|
||||||
if docker run --rm -v "$WORKSPACE_VOLUME":/data alpine sh -c '[ -d /data/coverage ] && ls -A /data/coverage | grep -q .' 2>/dev/null; then
|
COVERAGE_SRC=""
|
||||||
|
for candidate in /data/scripts/coverage /data/coverage; do
|
||||||
|
if docker run --rm -v "$WORKSPACE_VOLUME":/data alpine sh -c "[ -d \"$candidate\" ] && ls -A \"$candidate\" | grep -q ." 2>/dev/null; then
|
||||||
|
COVERAGE_SRC="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$COVERAGE_SRC" ]; then
|
||||||
mkdir -p "$REPORT_DIR/coverage"
|
mkdir -p "$REPORT_DIR/coverage"
|
||||||
docker run --rm -v "$WORKSPACE_VOLUME":/data alpine tar c -C /data/coverage . | tar x -C "$REPORT_DIR/coverage"
|
docker run --rm -v "$WORKSPACE_VOLUME":/data alpine tar c -C "$COVERAGE_SRC" . | tar x -C "$REPORT_DIR/coverage"
|
||||||
HAS_COVERAGE=true
|
HAS_COVERAGE=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ jobs:
|
|||||||
-v bats-workspace:/data \
|
-v bats-workspace:/data \
|
||||||
--entrypoint bash ${{ inputs.bats-image }} \
|
--entrypoint bash ${{ inputs.bats-image }} \
|
||||||
-c 'apk add -q lsof python3 jq curl ruby && cd /data && \
|
-c 'apk add -q lsof python3 jq curl ruby && cd /data && \
|
||||||
gem install bashcov 2>&1 | tail -1 && \
|
gem install bashcov -v 3.3.0 2>&1 | tail -1 && \
|
||||||
bashcov --include scripts/ -- bats tests/' \
|
bashcov --root /data/scripts/ -- bats tests/' \
|
||||||
> "reports/${GITHUB_SHA:0:8}/bats/results.txt" 2>&1
|
> "reports/${GITHUB_SHA:0:8}/bats/results.txt" 2>&1
|
||||||
BATS_EXIT=$?
|
BATS_EXIT=$?
|
||||||
bash .ci/.gitea/scripts/bats-coverage.sh bats-workspace "reports/${GITHUB_SHA:0:8}/bats"
|
bash .ci/.gitea/scripts/bats-coverage.sh bats-workspace "reports/${GITHUB_SHA:0:8}/bats"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ jobs:
|
|||||||
load-config:
|
load-config:
|
||||||
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@feature/pipeline-cleanup
|
uses: niko/gitea-ci-library/.gitea/workflows/config-provider.yml@feature/pipeline-cleanup
|
||||||
with:
|
with:
|
||||||
config_path: .gitea/workflows/feature-env.conf
|
config_path: .gitea/workflows/gitea-env.conf
|
||||||
|
|
||||||
feature:
|
feature:
|
||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Provider-repossa (`gitea-ci-library`) kansioiden omistajuus on seuraava:
|
|||||||
| Kansio / Tiedosto | Omistaja | Tyyppi |
|
| Kansio / Tiedosto | Omistaja | Tyyppi |
|
||||||
|-------------------|----------|--------|
|
|-------------------|----------|--------|
|
||||||
| `.gitea/workflows/` | Sekoitettu | Providerin reusable workflowt + consumerin pipeline |
|
| `.gitea/workflows/` | Sekoitettu | Providerin reusable workflowt + consumerin pipeline |
|
||||||
| `.gitea/workflows/feature-env.conf` | Consumer | KEY=VALUE config |
|
| `.gitea/workflows/gitea-env.conf` | Consumer | KEY=VALUE config |
|
||||||
| `.gitea/scripts/` | Consumer | Consumer-skriptit |
|
| `.gitea/scripts/` | Consumer | Consumer-skriptit |
|
||||||
| `scripts/` | Provider | Providerin sisäiset työkalut |
|
| `scripts/` | Provider | Providerin sisäiset työkalut |
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ Consumerin omat skriptit, osana consumerin pipeline-logiikkaa.
|
|||||||
Kutsutaan consumerin workflowista ilman tupla checkouttia:
|
Kutsutaan consumerin workflowista ilman tupla checkouttia:
|
||||||
`.gitea/scripts/bats-report.sh`.
|
`.gitea/scripts/bats-report.sh`.
|
||||||
|
|
||||||
## Consumerin `.gitea/workflows/feature-env.conf`
|
## Consumerin `.gitea/workflows/gitea-env.conf`
|
||||||
|
|
||||||
Consumerin konfiguraatiotiedosto. Providerin `config-provider.yml`
|
Consumerin konfiguraatiotiedosto. Providerin `config-provider.yml`
|
||||||
lukee tämän ja muuntaa JSONiksi, mutta consumer omistaa sisällön.
|
lukee tämän ja muuntaa JSONiksi, mutta consumer omistaa sisällön.
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ mekanismin ketjuttamista:
|
|||||||
Ketju toimii näin:
|
Ketju toimii näin:
|
||||||
|
|
||||||
```
|
```
|
||||||
feature-env.conf → config-provider.yml → env_json (yksi JSON-string)
|
gitea-env.conf → config-provider.yml → env_json (yksi JSON-string)
|
||||||
(1) (2)
|
(1) (2)
|
||||||
↓
|
↓
|
||||||
ci.yml with: env_json
|
ci.yml with: env_json
|
||||||
@@ -80,7 +80,7 @@ feature-env.conf → config-provider.yml → env_json (yksi JSON-string)
|
|||||||
```
|
```
|
||||||
|
|
||||||
Vaiheet:
|
Vaiheet:
|
||||||
1. Consumer määrittelee arvot `feature-env.conf`:ssä (KEY=VALUE)
|
1. Consumer määrittelee arvot `gitea-env.conf`:ssä (KEY=VALUE)
|
||||||
2. `config-provider.yml` lukee confin ja tuottaa yhden JSON-stringin outputina
|
2. `config-provider.yml` lukee confin ja tuottaa yhden JSON-stringin outputina
|
||||||
3. `ci.yml` välittää JSONin `needs` + `with:` -ketjulla
|
3. `ci.yml` välittää JSONin `needs` + `with:` -ketjulla
|
||||||
4. `build-feature.yml` purkaa arvot workflow `env:`-tasolle `fromJson()`:lla
|
4. `build-feature.yml` purkaa arvot workflow `env:`-tasolle `fromJson()`:lla
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
CONF_FILE=".gitea/workflows/feature-env.conf"
|
CONF_FILE="${CI_CONF_FILE:-.gitea/workflows/gitea-env.conf}"
|
||||||
ERRORS=0
|
ERRORS=0
|
||||||
|
|
||||||
[ -f "$CONF_FILE" ] || { echo "ERROR: $CONF_FILE not found — checkout missing?" >&2; exit 1; }
|
[ -f "$CONF_FILE" ] || { echo "ERROR: $CONF_FILE not found — checkout missing?" >&2; exit 1; }
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
export CONF_FILE=$(mktemp)
|
||||||
|
export CI_CONF_FILE="$CONF_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
rm -f "$CONF_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing config file → exit 1" {
|
||||||
|
export CI_CONF_FILE="/nonexistent/path/$(date +%s).conf"
|
||||||
|
run bash scripts/ci-validate.sh
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"ERROR"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "empty value in config → exit 1" {
|
||||||
|
cat > "$CONF_FILE" <<EOF
|
||||||
|
SOME_KEY=
|
||||||
|
EOF
|
||||||
|
run bash scripts/ci-validate.sh
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"empty"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "invalid URL in config → exit 1" {
|
||||||
|
cat > "$CONF_FILE" <<EOF
|
||||||
|
API_URL=not-a-url
|
||||||
|
EOF
|
||||||
|
run bash scripts/ci-validate.sh
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"URL"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GITEA_TOKEN secret → exit 1" {
|
||||||
|
cat > "$CONF_FILE" <<EOF
|
||||||
|
SOME_KEY=ok
|
||||||
|
EOF
|
||||||
|
unset GITEA_TOKEN
|
||||||
|
run bash scripts/ci-validate.sh
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GITEA_TOKEN"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GIT_PAGES_PUBLISH_TOKEN secret → exit 1" {
|
||||||
|
cat > "$CONF_FILE" <<EOF
|
||||||
|
SOME_KEY=ok
|
||||||
|
EOF
|
||||||
|
export GITEA_TOKEN="sometoken"
|
||||||
|
unset GIT_PAGES_PUBLISH_TOKEN
|
||||||
|
run bash scripts/ci-validate.sh
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GIT_PAGES_PUBLISH_TOKEN"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "valid config and all secrets → exit 0" {
|
||||||
|
cat > "$CONF_FILE" <<EOF
|
||||||
|
API_URL=https://example.com
|
||||||
|
ANOTHER=https://test.fi
|
||||||
|
EOF
|
||||||
|
export GITEA_TOKEN="sometoken"
|
||||||
|
export GIT_PAGES_PUBLISH_TOKEN="sometoken"
|
||||||
|
run bash scripts/ci-validate.sh
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "comment and blank lines are ignored → exit 0" {
|
||||||
|
cat > "$CONF_FILE" <<EOF
|
||||||
|
# this is a comment
|
||||||
|
|
||||||
|
VALID_URL=https://example.com
|
||||||
|
EOF
|
||||||
|
export GITEA_TOKEN="sometoken"
|
||||||
|
export GIT_PAGES_PUBLISH_TOKEN="sometoken"
|
||||||
|
run bash scripts/ci-validate.sh
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
||||||
@@ -120,6 +120,17 @@ teardown() {
|
|||||||
[ "$status" -eq 1 ]
|
[ "$status" -eq 1 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "dispatch: no workflow run found after dispatch → exit 1" {
|
||||||
|
mock_set_sequence '[
|
||||||
|
{"code":201},
|
||||||
|
{"code":200,"body":{"workflow_runs":[]}}
|
||||||
|
]'
|
||||||
|
mock_start
|
||||||
|
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" '{}' "http://localhost:18080" "test-token-abc123"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"ERROR"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
@test "missing inputs_json argument → exit 1" {
|
@test "missing inputs_json argument → exit 1" {
|
||||||
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" "" "http://localhost:18080" "test-token-abc123"
|
run bash scripts/dispatch-workflow.sh "test-owner/test-repo" "test.yml" "main" "" "http://localhost:18080" "test-token-abc123"
|
||||||
[ "$status" -eq 1 ]
|
[ "$status" -eq 1 ]
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ _kill_port() {
|
|||||||
local pids
|
local pids
|
||||||
pids=$(lsof -ti ":$MOCK_PORT" 2>/dev/null) || true
|
pids=$(lsof -ti ":$MOCK_PORT" 2>/dev/null) || true
|
||||||
[ -n "$pids" ] && kill -9 $pids 2>/dev/null || true
|
[ -n "$pids" ] && kill -9 $pids 2>/dev/null || true
|
||||||
sleep 0.3
|
sleep 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
_wait_port_free() {
|
_wait_port_free() {
|
||||||
local i=0
|
local i=0
|
||||||
while lsof -ti ":$MOCK_PORT" >/dev/null 2>&1 && [ $i -lt 30 ]; do
|
while lsof -ti ":$MOCK_PORT" >/dev/null 2>&1 && [ $i -lt 50 ]; do
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
i=$((i + 1))
|
i=$((i + 1))
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class H(http.server.BaseHTTPRequestHandler):
|
|||||||
def _log_request(self, method):
|
def _log_request(self, method):
|
||||||
path = self.path
|
path = self.path
|
||||||
content_len = int(self.headers.get('Content-Length', 0))
|
content_len = int(self.headers.get('Content-Length', 0))
|
||||||
body = self.rfile.read(content_len).decode() if content_len else ''
|
body = self.rfile.read(content_len).decode(errors='replace') if content_len else ''
|
||||||
line = f'{method} {path}\n{body}\n'
|
line = f'{method} {path}\n{body}\n'
|
||||||
with open(REQ_FILE, 'a') as f:
|
with open(REQ_FILE, 'a') as f:
|
||||||
f.write(line)
|
f.write(line)
|
||||||
@@ -67,6 +67,14 @@ class H(http.server.BaseHTTPRequestHandler):
|
|||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(body.encode())
|
self.wfile.write(body.encode())
|
||||||
|
|
||||||
|
def do_PATCH(self):
|
||||||
|
self._log_request('PATCH')
|
||||||
|
code, body = self._get_response()
|
||||||
|
self.send_response(code)
|
||||||
|
self.send_header('Content-Type', 'application/json')
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(body.encode())
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
def log_message(self, format, *args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
source tests/helpers/mock-api.sh
|
||||||
|
export GITEA_API_URL="http://localhost:18080"
|
||||||
|
export PAGES_HOST="reports.example.com"
|
||||||
|
export GIT_PAGES_PUBLISH_URL="http://localhost:18080/"
|
||||||
|
export GIT_PAGES_PUBLISH_TOKEN="publish-token-abc"
|
||||||
|
export GITHUB_REPOSITORY="test-owner/test-repo"
|
||||||
|
export GITHUB_SHA="abc123def456789012345678901234567890abcd"
|
||||||
|
|
||||||
|
REPORT_DIR="reports/abc123de/unit-tests"
|
||||||
|
mkdir -p "$REPORT_DIR"
|
||||||
|
echo "<html>test</html>" > "$REPORT_DIR/index.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
mock_stop
|
||||||
|
rm -rf "reports/abc123de"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing suite_path argument → exit 1" {
|
||||||
|
run bash scripts/publish-git-pages.sh ""
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"ERROR"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GITEA_API_URL → exit 1" {
|
||||||
|
unset GITEA_API_URL
|
||||||
|
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GITEA_API_URL"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing PAGES_HOST → exit 1" {
|
||||||
|
unset PAGES_HOST
|
||||||
|
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"PAGES_HOST"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GIT_PAGES_PUBLISH_URL → exit 1" {
|
||||||
|
unset GIT_PAGES_PUBLISH_URL
|
||||||
|
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GIT_PAGES_PUBLISH_URL"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GIT_PAGES_PUBLISH_TOKEN → exit 1" {
|
||||||
|
unset GIT_PAGES_PUBLISH_TOKEN
|
||||||
|
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GIT_PAGES_PUBLISH_TOKEN"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GITHUB_REPOSITORY → exit 1" {
|
||||||
|
unset GITHUB_REPOSITORY
|
||||||
|
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GITHUB_REPOSITORY"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "suite path is not a directory → exit 1" {
|
||||||
|
run bash scripts/publish-git-pages.sh "nonexistent"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"not a directory"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "valid publish returns report base URL" {
|
||||||
|
mock_set_sequence '[
|
||||||
|
{"code":200,"body":"published"}
|
||||||
|
]'
|
||||||
|
mock_start
|
||||||
|
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" == "https://reports.example.com/test-owner/test-repo/reports/abc123de" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "publish with suite subpath" {
|
||||||
|
mkdir -p "reports/abc123de/sub/suite"
|
||||||
|
echo "sub" > "reports/abc123de/sub/suite/result.html"
|
||||||
|
mock_set_sequence '[
|
||||||
|
{"code":200,"body":"published"}
|
||||||
|
]'
|
||||||
|
mock_start
|
||||||
|
run bash scripts/publish-git-pages.sh "sub/suite"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" == "https://reports.example.com/test-owner/test-repo/reports/abc123de" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "git-pages returns HTTP 500 → exit 1" {
|
||||||
|
mock_set_sequence '[
|
||||||
|
{"code":500,"body":"internal error"}
|
||||||
|
]'
|
||||||
|
mock_start
|
||||||
|
run bash scripts/publish-git-pages.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"500"* ]]
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
export GITEA_API_URL="http://localhost:18080"
|
||||||
|
export GITEA_TOKEN="test-token-abc"
|
||||||
|
export PAGES_HOST="reports.example.com"
|
||||||
|
export GIT_PAGES_PUBLISH_URL="http://localhost:18080/"
|
||||||
|
export GIT_PAGES_PUBLISH_TOKEN="publish-token-abc"
|
||||||
|
export GITHUB_REPOSITORY="test-owner/test-repo"
|
||||||
|
export GITHUB_SHA="abc123def456789012345678901234567890abcd"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing suite_path argument → exit 1" {
|
||||||
|
run bash scripts/publish.sh ""
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"ERROR"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GITEA_API_URL → exit 1" {
|
||||||
|
unset GITEA_API_URL
|
||||||
|
run bash scripts/publish.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GITEA_API_URL"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GITEA_TOKEN → exit 1" {
|
||||||
|
unset GITEA_TOKEN
|
||||||
|
run bash scripts/publish.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GITEA_TOKEN"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing PAGES_HOST → exit 1" {
|
||||||
|
unset PAGES_HOST
|
||||||
|
run bash scripts/publish.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"PAGES_HOST"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GIT_PAGES_PUBLISH_URL → exit 1" {
|
||||||
|
unset GIT_PAGES_PUBLISH_URL
|
||||||
|
run bash scripts/publish.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GIT_PAGES_PUBLISH_URL"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "missing GIT_PAGES_PUBLISH_TOKEN → exit 1" {
|
||||||
|
unset GIT_PAGES_PUBLISH_TOKEN
|
||||||
|
run bash scripts/publish.sh "unit-tests"
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "$output" == *"GIT_PAGES_PUBLISH_TOKEN"* ]]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user