Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94ff1d12e3 | ||
|
|
cc9c3dea88 | ||
|
|
815cd85b9a | ||
|
|
9021eddbc3 | ||
|
|
2adf17c307 | ||
|
|
1add5b5cd7 | ||
|
|
34f10211ab | ||
|
|
02447f2946 |
No files matched your search
@@ -141,6 +141,7 @@ jobs:
|
||||
export PATH="$tools_dir:$PATH"
|
||||
ruff check .
|
||||
ruff format --check .
|
||||
python3 -m unittest discover -s tests -v
|
||||
|
||||
lint-yaml:
|
||||
runs-on: [self-hosted, linux, arch, homelab]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
EDU_LOGIN=your_edu_login_here
|
||||
EDU_PASSWORD=your_edu_password_here
|
||||
EDU_URL_LOGIN=https://edu.edu.vn.ua/user/login
|
||||
EDU_URL_VERIFY=https://edu.edu.vn.ua/course/userlist
|
||||
PHPSESSID_INTERVAL=10
|
||||
KEEPER_LOGIN=your_edu_login_here
|
||||
KEEPER_PASSWORD=your_edu_password_here
|
||||
EDU_URL_BASE=https://edu.edu.vn.ua
|
||||
EDU_URL_LOGIN=/user/login
|
||||
EDU_URL_COURSES=/course/userlist
|
||||
KEEPER_INTERVAL=10
|
||||
USER_AGENT="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
|
||||
WEBINAR_URL=https://edu.edu.vn.ua/webinar/useractive
|
||||
EDU_URL_WEBINAR=/webinar/useractive
|
||||
WEBINAR_CHECK_INTERVAL=60
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
|
||||
@@ -48,17 +48,43 @@ def touch_success_file():
|
||||
logger.error(f'Failed to touch success file: {e}')
|
||||
|
||||
|
||||
def refresh_session(session, redis_client):
|
||||
"""Publish a verified cookie with a lifetime tied to the refresh interval."""
|
||||
login_response = session.post(
|
||||
URL_LOGIN, data={'login': LOGIN, 'password': PASSWORD}, allow_redirects=True, timeout=(10, 30)
|
||||
)
|
||||
login_response.raise_for_status()
|
||||
verify_response = session.get(URL_VERIFY, allow_redirects=False, timeout=(10, 30))
|
||||
if verify_response.status_code != 200:
|
||||
logger.warning('Session verification failed (HTTP %s)', verify_response.status_code)
|
||||
return False
|
||||
phpsessid = session.cookies.get('PHPSESSID')
|
||||
if not phpsessid:
|
||||
logger.warning('Verified response did not provide a PHPSESSID cookie')
|
||||
return False
|
||||
redis_client.set('EDU_PHPSESSID', phpsessid, ex=INTERVAL * 120)
|
||||
touch_success_file()
|
||||
logger.info('Verified session saved to Redis')
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
if not LOGIN or not PASSWORD:
|
||||
raise ValueError('KEEPER_LOGIN and KEEPER_PASSWORD must be set')
|
||||
if INTERVAL <= 0:
|
||||
raise ValueError('KEEPER_INTERVAL must be a positive number of minutes')
|
||||
logger.info('Starting Session Keeper Bot')
|
||||
|
||||
# Connect to Redis
|
||||
try:
|
||||
redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)
|
||||
redis_client = redis.Redis(
|
||||
host=REDIS_HOST, port=REDIS_PORT, decode_responses=True, socket_connect_timeout=5, socket_timeout=5
|
||||
)
|
||||
redis_client.ping()
|
||||
logger.info(f'Connected to Redis at {REDIS_HOST}:{REDIS_PORT}')
|
||||
except Exception as e:
|
||||
logger.error(f'Failed to connect to Redis: {e}')
|
||||
return
|
||||
raise
|
||||
|
||||
session = requests.Session()
|
||||
|
||||
@@ -83,44 +109,7 @@ def main():
|
||||
|
||||
while True:
|
||||
try:
|
||||
logger.info('Attempting login...')
|
||||
|
||||
# Login payload
|
||||
payload = {'login': LOGIN, 'password': PASSWORD}
|
||||
|
||||
# Perform Login
|
||||
# Note: The user request shows a POST to /user/login with form data
|
||||
# We need to make sure we handle the PHPSESSID correctly.
|
||||
# If we already have a PHPSESSID, requests will send it.
|
||||
|
||||
login_response = session.post(URL_LOGIN, data=payload, allow_redirects=True)
|
||||
|
||||
logger.info(f'Login Response Status: {login_response.status_code}')
|
||||
logger.info(f'Cookies after login: {session.cookies.get_dict()}')
|
||||
|
||||
# Verify Session
|
||||
logger.info('Verifying session...')
|
||||
verify_response = session.get(URL_VERIFY, allow_redirects=False)
|
||||
|
||||
logger.info(f'Verify Response Status: {verify_response.status_code}')
|
||||
|
||||
if verify_response.status_code == 200:
|
||||
logger.info('Session verification SUCCESS (200 OK).')
|
||||
touch_success_file()
|
||||
|
||||
# Save PHPSESSID to Redis
|
||||
phpsessid = session.cookies.get('PHPSESSID')
|
||||
if phpsessid:
|
||||
try:
|
||||
redis_client.set('EDU_PHPSESSID', phpsessid)
|
||||
logger.info(f'Saved PHPSESSID to Redis: {phpsessid}')
|
||||
except Exception as e:
|
||||
logger.error(f'Failed to save PHPSESSID to Redis: {e}')
|
||||
elif verify_response.status_code == 302:
|
||||
logger.warning('Session verification FAILED (302 Redirect). Session might be invalid.')
|
||||
else:
|
||||
logger.warning(f'Session verification returned unexpected status: {verify_response.status_code}')
|
||||
|
||||
refresh_session(session, redis_client)
|
||||
except Exception as e:
|
||||
logger.error(f'An error occurred: {e}')
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
SECRET_ENCRYPTION_KEY="REPLACE_ME"
|
||||
TZ="Europe/Bratislava"
|
||||
PUID="1000"
|
||||
PGID="1000"
|
||||
@@ -0,0 +1,38 @@
|
||||
services:
|
||||
homarr:
|
||||
container_name: homarr
|
||||
image: ghcr.io/homarr-labs/homarr:v2.1.2
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./appdata:/appdata
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./kubeconfig:/app/config/kubeconfig:ro
|
||||
env_file: .env
|
||||
ports:
|
||||
- 80:7575
|
||||
- 81:3000
|
||||
environment:
|
||||
- TZ=${TZ:-Europe/Bratislava}
|
||||
- TURBO_TELEMETRY_DISABLED=1
|
||||
- KUBECONFIG=/app/config/kubeconfig
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.homarr.loadbalancer.server.port=7575"
|
||||
|
||||
# Prod Router
|
||||
- "traefik.http.routers.homarr.rule=Host(`homarr.forust.xyz`)"
|
||||
- "traefik.http.routers.homarr.entrypoints=websecure"
|
||||
- "traefik.http.routers.homarr.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.homarr-local.rule=Host(`homarr.workstation.internal`)"
|
||||
- "traefik.http.routers.homarr-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.homarr-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.homarr-dev.rule=Host(`homarr.gigaforust.internal`)"
|
||||
- "traefik.http.routers.homarr-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.homarr-dev.tls=true"
|
||||
networks:
|
||||
- proxy
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
@@ -0,0 +1,28 @@
|
||||
# apiVersion: cert-manager.io/v1
|
||||
# kind: Certificate
|
||||
# metadata:
|
||||
# name: home-prod-tls
|
||||
# namespace: homarr
|
||||
# spec:
|
||||
# secretName: home-prod-tls
|
||||
# dnsNames:
|
||||
# - home.forust.xyz
|
||||
# issuerRef:
|
||||
# name: letsencrypt-prod
|
||||
# kind: ClusterIssuer
|
||||
# ---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: internal-wildcard-tls
|
||||
namespace: homarr
|
||||
spec:
|
||||
secretName: internal-wildcard-tls
|
||||
dnsNames:
|
||||
- "*.workstation.internal"
|
||||
- "*.gigaforust.internal"
|
||||
- workstation.internal
|
||||
- gigaforust.internal
|
||||
issuerRef:
|
||||
name: internal-ca
|
||||
kind: ClusterIssuer
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: homarr-config
|
||||
namespace: homarr
|
||||
data:
|
||||
TZ: "Europe/Bratislava"
|
||||
TURBO_TELEMETRY_DISABLED: "1"
|
||||
ENABLE_KUBERNETES: "true"
|
||||
@@ -0,0 +1,81 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: homarr-service
|
||||
namespace: homarr
|
||||
spec:
|
||||
selector:
|
||||
app: homarr
|
||||
ports:
|
||||
- port: 7575
|
||||
targetPort: 7575
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: homarr-deployment
|
||||
namespace: homarr
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: homarr
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: homarr
|
||||
spec:
|
||||
serviceAccountName: homarr
|
||||
containers:
|
||||
- name: homarr
|
||||
image: ghcr.io/homarr-labs/homarr:v2.1.2
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: homarr-config
|
||||
- secretRef:
|
||||
name: homarr-secrets
|
||||
ports:
|
||||
- containerPort: 7575
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 7575
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
failureThreshold: 6
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 7575
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
failureThreshold: 3
|
||||
volumeMounts:
|
||||
- name: homarr-data
|
||||
mountPath: /appdata
|
||||
resources:
|
||||
requests:
|
||||
cpu: "250m"
|
||||
memory: "350Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "700Mi"
|
||||
volumes:
|
||||
- name: homarr-data
|
||||
persistentVolumeClaim:
|
||||
claimName: homarr-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: homarr-pvc
|
||||
namespace: homarr
|
||||
spec:
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
volumeMode: Filesystem
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
@@ -0,0 +1,33 @@
|
||||
# apiVersion: traefik.io/v1alpha1
|
||||
# kind: IngressRoute
|
||||
# metadata:
|
||||
# name: homarr-prod
|
||||
# namespace: homarr
|
||||
# spec:
|
||||
# entryPoints:
|
||||
# - websecure
|
||||
# routes:
|
||||
# - match: Host(`home.forust.xyz`)
|
||||
# kind: Rule
|
||||
# services:
|
||||
# - name: homarr-service
|
||||
# port: 7575
|
||||
# tls:
|
||||
# secretName: home-prod-tls
|
||||
# ---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: homarr-local
|
||||
namespace: homarr
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`home.workstation.internal`) || Host(`home.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: homarr-service
|
||||
port: 7575
|
||||
tls:
|
||||
secretName: internal-wildcard-tls
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: homarr
|
||||
@@ -0,0 +1,58 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: homarr
|
||||
namespace: homarr
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: homarr-readonly
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- pods
|
||||
- services
|
||||
- endpoints
|
||||
- namespaces
|
||||
- nodes
|
||||
- configmaps
|
||||
- persistentvolumeclaims
|
||||
- events
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["apps"]
|
||||
resources:
|
||||
- deployments
|
||||
- statefulsets
|
||||
- daemonsets
|
||||
- replicasets
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources:
|
||||
- ingresses
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["traefik.io"]
|
||||
resources:
|
||||
- ingressroutes
|
||||
- ingressroutetcps
|
||||
- ingressrouteudps
|
||||
- middlewares
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["metrics.k8s.io"]
|
||||
resources:
|
||||
- pods
|
||||
- nodes
|
||||
verbs: ["get", "list"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: homarr-readonly
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: homarr-readonly
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: homarr
|
||||
namespace: homarr
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: homarr-secrets
|
||||
namespace: homarr
|
||||
type: Opaque
|
||||
stringData:
|
||||
# openssl rand -hex 32
|
||||
SECRET_ENCRYPTION_KEY: "REPLACE_ME"
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
n8n:
|
||||
image: docker.n8n.io/n8nio/n8n:2.42.2
|
||||
image: docker.n8n.io/n8nio/n8n:2.42.3
|
||||
container_name: n8n
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: n8n
|
||||
image: docker.n8n.io/n8nio/n8n:2.42.2
|
||||
image: docker.n8n.io/n8nio/n8n:2.42.3
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: n8n-config
|
||||
|
||||
@@ -218,6 +218,34 @@ data:
|
||||
"matchUpdateTypes": ["patch"],
|
||||
"groupName": "all patch updates",
|
||||
"groupSlug": "all-patch"
|
||||
},
|
||||
{
|
||||
"description": "Python Y-bumps break compat (3.11->3.12->3.13->3.14) - keep the base image out of the shared minor/patch groups, review every bump separately. Placed last so its groupName wins.",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": ["python"],
|
||||
"groupName": "python base image",
|
||||
"groupSlug": "python",
|
||||
"automerge": false
|
||||
},
|
||||
{
|
||||
"description": "Rolling/floating tags (streaming stack, nextcloud beta, kubectl latest) - never automerge, every bump is a manual review. Placed last so automerge:false wins over the shared digest/patch rule.",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": [
|
||||
"lscr.io/linuxserver/jellyfin",
|
||||
"lscr.io/linuxserver/qbittorrent",
|
||||
"lscr.io/linuxserver/sonarr",
|
||||
"lscr.io/linuxserver/radarr",
|
||||
"lscr.io/linuxserver/prowlarr",
|
||||
"lscr.io/linuxserver/bazarr",
|
||||
"ghcr.io/seerr-team/seerr",
|
||||
"fallenbagel/jellyseerr",
|
||||
"ghcr.io/lampac-nextgen/lampac",
|
||||
"ghcr.io/nextcloud-releases/all-in-one",
|
||||
"alpine/kubectl"
|
||||
],
|
||||
"groupName": "floating images - manual",
|
||||
"groupSlug": "floating-manual",
|
||||
"automerge": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -19,7 +19,7 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: renovate
|
||||
image: renovate/renovate:44.132.2
|
||||
image: renovate/renovate:44.136.0
|
||||
env:
|
||||
- name: RENOVATE_PLATFORM
|
||||
value: gitea
|
||||
|
||||
@@ -207,6 +207,34 @@
|
||||
"matchUpdateTypes": ["patch"],
|
||||
"groupName": "all patch updates",
|
||||
"groupSlug": "all-patch"
|
||||
},
|
||||
{
|
||||
"description": "Python Y-bumps break compat (3.11->3.12->3.13->3.14) - keep the base image out of the shared minor/patch groups, review every bump separately. Placed last so its groupName wins.",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": ["python"],
|
||||
"groupName": "python base image",
|
||||
"groupSlug": "python",
|
||||
"automerge": false
|
||||
},
|
||||
{
|
||||
"description": "Rolling/floating tags (streaming stack, nextcloud beta, kubectl latest) - never automerge, every bump is a manual review. Placed last so automerge:false wins over the shared digest/patch rule.",
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": [
|
||||
"lscr.io/linuxserver/jellyfin",
|
||||
"lscr.io/linuxserver/qbittorrent",
|
||||
"lscr.io/linuxserver/sonarr",
|
||||
"lscr.io/linuxserver/radarr",
|
||||
"lscr.io/linuxserver/prowlarr",
|
||||
"lscr.io/linuxserver/bazarr",
|
||||
"ghcr.io/seerr-team/seerr",
|
||||
"fallenbagel/jellyseerr",
|
||||
"ghcr.io/lampac-nextgen/lampac",
|
||||
"ghcr.io/nextcloud-releases/all-in-one",
|
||||
"alpine/kubectl"
|
||||
],
|
||||
"groupName": "floating images - manual",
|
||||
"groupSlug": "floating-manual",
|
||||
"automerge": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
jellyfin:
|
||||
image: lscr.io/linuxserver/jellyfin:latest
|
||||
image: lscr.io/linuxserver/jellyfin:version-12.1ubu2604
|
||||
container_name: jellyfin
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@@ -19,7 +19,7 @@ services:
|
||||
- streaming
|
||||
|
||||
qbittorrent:
|
||||
image: lscr.io/linuxserver/qbittorrent:latest
|
||||
image: lscr.io/linuxserver/qbittorrent:5.2.4
|
||||
container_name: qbittorrent
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@@ -38,7 +38,7 @@ services:
|
||||
- streaming
|
||||
|
||||
sonarr:
|
||||
image: lscr.io/linuxserver/sonarr:latest
|
||||
image: lscr.io/linuxserver/sonarr:4.0.20
|
||||
container_name: sonarr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@@ -55,7 +55,7 @@ services:
|
||||
- streaming
|
||||
|
||||
radarr:
|
||||
image: lscr.io/linuxserver/radarr:latest
|
||||
image: lscr.io/linuxserver/radarr:6.4.4
|
||||
container_name: radarr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@@ -72,7 +72,7 @@ services:
|
||||
- streaming
|
||||
|
||||
prowlarr:
|
||||
image: lscr.io/linuxserver/prowlarr:latest
|
||||
image: lscr.io/linuxserver/prowlarr:2.6.5
|
||||
container_name: prowlarr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@@ -100,7 +100,7 @@ services:
|
||||
- streaming
|
||||
|
||||
bazarr:
|
||||
image: lscr.io/linuxserver/bazarr:latest
|
||||
image: lscr.io/linuxserver/bazarr:1.6.2
|
||||
container_name: bazarr
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
termix:
|
||||
image: ghcr.io/lukegus/termix:2.9.0
|
||||
image: ghcr.io/lukegus/termix:2.9.1
|
||||
container_name: termix
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
|
||||
@@ -29,7 +29,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: termix
|
||||
image: ghcr.io/lukegus/termix:2.9.0
|
||||
image: ghcr.io/lukegus/termix:2.9.1
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: termix-config
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Session publication checks without Redis, the EDU website, or credentials."""
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parents[1] / 'edu_master/phpsessid-bot/bot.py'
|
||||
spec = importlib.util.spec_from_file_location('session_keeper', SCRIPT)
|
||||
bot = importlib.util.module_from_spec(spec)
|
||||
with patch.dict(sys.modules, {'redis': Mock(), 'requests': Mock()}):
|
||||
spec.loader.exec_module(bot)
|
||||
|
||||
|
||||
class SessionKeeperTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.session = Mock()
|
||||
self.session.get.return_value.status_code = 200
|
||||
self.session.cookies.get.return_value = 'test-cookie'
|
||||
self.redis = Mock()
|
||||
self.touch = patch.object(bot, 'touch_success_file').start()
|
||||
self.addCleanup(patch.stopall)
|
||||
|
||||
def test_verified_cookie_expires_and_is_not_logged(self):
|
||||
with self.assertLogs(bot.logger, level='INFO') as logs:
|
||||
self.assertTrue(bot.refresh_session(self.session, self.redis))
|
||||
self.redis.set.assert_called_once_with('EDU_PHPSESSID', 'test-cookie', ex=bot.INTERVAL * 120)
|
||||
self.touch.assert_called_once()
|
||||
self.assertNotIn('test-cookie', '\n'.join(logs.output))
|
||||
self.assertEqual(self.session.post.call_args.kwargs['timeout'], (10, 30))
|
||||
self.assertEqual(self.session.get.call_args.kwargs['timeout'], (10, 30))
|
||||
|
||||
def test_redirect_does_not_publish(self):
|
||||
self.session.get.return_value.status_code = 302
|
||||
self.assertFalse(bot.refresh_session(self.session, self.redis))
|
||||
self.redis.set.assert_not_called()
|
||||
self.touch.assert_not_called()
|
||||
|
||||
def test_missing_cookie_does_not_mark_success(self):
|
||||
self.session.cookies.get.return_value = None
|
||||
self.assertFalse(bot.refresh_session(self.session, self.redis))
|
||||
self.redis.set.assert_not_called()
|
||||
self.touch.assert_not_called()
|
||||
|
||||
def test_redis_failure_does_not_mark_success(self):
|
||||
self.redis.set.side_effect = OSError('redis unavailable')
|
||||
with self.assertRaises(OSError):
|
||||
bot.refresh_session(self.session, self.redis)
|
||||
self.touch.assert_not_called()
|
||||
|
||||
def test_http_timeout_does_not_publish(self):
|
||||
self.session.post.side_effect = TimeoutError('EDU unavailable')
|
||||
with self.assertRaises(TimeoutError):
|
||||
bot.refresh_session(self.session, self.redis)
|
||||
self.redis.set.assert_not_called()
|
||||
self.touch.assert_not_called()
|
||||
|
||||
def test_missing_credentials_fail_before_network_access(self):
|
||||
with patch.object(bot, 'LOGIN', None), self.assertRaises(ValueError):
|
||||
bot.main()
|
||||
|
||||
def test_nonpositive_interval_fails_before_network_access(self):
|
||||
with (
|
||||
patch.object(bot, 'LOGIN', 'test'),
|
||||
patch.object(bot, 'PASSWORD', 'test'),
|
||||
patch.object(bot, 'INTERVAL', 0),
|
||||
self.assertRaises(ValueError),
|
||||
):
|
||||
bot.main()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -94,7 +94,7 @@ ports:
|
||||
exposedPort: 8080
|
||||
protocol: TCP
|
||||
expose:
|
||||
default: false
|
||||
default: true
|
||||
http:
|
||||
aliasHeadersStrategy: delete
|
||||
ssh:
|
||||
|
||||
Reference in new issue
Block a user