One app fixture for the tests, and ViewModels built where they are used
CI & Build / Python lint (push) Successful in 2s
CI & Build / Build now, or wait for Android? (push) Successful in 2s
Android / Build, or is the channel already serving this? (push) Successful in 3s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Skipped
Desktop (Tauri) / Tauri desktop (Linux) (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Skipped
Desktop (Tauri) / Update manifest (push) Skipped
CI & Build / Web typecheck and unit tests (push) Successful in 8s
CI & Build / Python tests (push) Successful in 13s
Android / Core and FFI clippy and tests (push) Successful in 25s
CI & Build / integration (push) Successful in 1m31s
CI & Build / Build & push image (push) Skipped
Android / Kotlin + Rust (APK) (push) Successful in 6m41s
Android / Build the server image (push) Successful in 1s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Build now, or wait for Android? (push) Successful in 2s
Android / Build, or is the channel already serving this? (push) Successful in 3s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Skipped
Desktop (Tauri) / Tauri desktop (Linux) (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Skipped
Desktop (Tauri) / Update manifest (push) Skipped
CI & Build / Web typecheck and unit tests (push) Successful in 8s
CI & Build / Python tests (push) Successful in 13s
Android / Core and FFI clippy and tests (push) Successful in 25s
CI & Build / integration (push) Successful in 1m31s
CI & Build / Build & push image (push) Skipped
Android / Kotlin + Rust (APK) (push) Successful in 6m41s
Android / Build the server image (push) Successful in 1s
The tests' `app` fixture, the same two lines in eleven files, now lives once in
tests/conftest.py.
On Android each ViewModel carried a companion `factory` that only wrapped its
constructor in viewModelFactory { initializer { … } }. MainActivity now builds
each one with Compose's viewModel { … } builder, so the five factories and their
imports are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -219,15 +219,16 @@ private fun App(
|
|||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val board: BoardViewModel =
|
val board: BoardViewModel =
|
||||||
viewModel(
|
viewModel {
|
||||||
factory =
|
BoardViewModel(
|
||||||
BoardViewModel.factory(core, readFile = files::read) {
|
core,
|
||||||
// Any store write can have moved the next reminder. Called on
|
// Any store write can have moved the next reminder. Called on the IO
|
||||||
// the IO dispatcher by the view model, which is where it has to
|
// dispatcher by the view model, which is where it has to be — this
|
||||||
// be — this reads every note carrying a reminder.
|
// reads every note carrying a reminder.
|
||||||
Reminders.refresh(context, core)
|
onRemindersChanged = { Reminders.refresh(context, core) },
|
||||||
},
|
readFile = files::read,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Consumed, not just read: without clearing it, every later recomposition
|
// Consumed, not just read: without clearing it, every later recomposition
|
||||||
// would reopen the same note and make the editor impossible to leave.
|
// would reopen the same note and make the editor impossible to leave.
|
||||||
@@ -251,7 +252,7 @@ private fun App(
|
|||||||
// A pull can rewrite every note the board is holding, so a sync that changed
|
// A pull can rewrite every note the board is holding, so a sync that changed
|
||||||
// anything tells it to reload. Wired here, at the one place that owns both.
|
// anything tells it to reload. Wired here, at the one place that owns both.
|
||||||
val sync: SyncViewModel =
|
val sync: SyncViewModel =
|
||||||
viewModel(factory = SyncViewModel.factory(core, onStoreChanged = board::refresh))
|
viewModel { SyncViewModel(core, onStoreChanged = board::refresh) }
|
||||||
|
|
||||||
// Screen visibility is view STATE, not view-model state: it is about what is on
|
// Screen visibility is view STATE, not view-model state: it is about what is on
|
||||||
// the display, and nothing in the store cares. Saveable so a rotation does not
|
// the display, and nothing in the store cares. Saveable so a rotation does not
|
||||||
@@ -266,14 +267,14 @@ private fun App(
|
|||||||
// board may be LOOKING at one that a delete or a merge just removed. Both are
|
// board may be LOOKING at one that a delete or a merge just removed. Both are
|
||||||
// `refreshLabels`, which also leaves a lens whose tag stopped existing.
|
// `refreshLabels`, which also leaves a lens whose tag stopped existing.
|
||||||
val tags: TagsViewModel =
|
val tags: TagsViewModel =
|
||||||
viewModel(factory = TagsViewModel.factory(core, onStoreChanged = board::refreshLabels))
|
viewModel { TagsViewModel(core, onStoreChanged = board::refreshLabels) }
|
||||||
|
|
||||||
// A share landing changes the note's `shared` flag, which the board's card chip
|
// A share landing changes the note's `shared` flag, which the board's card chip
|
||||||
// reads, so the board reloads after one.
|
// reads, so the board reloads after one.
|
||||||
val share: ShareViewModel =
|
val share: ShareViewModel =
|
||||||
viewModel(factory = ShareViewModel.factory(core, onStoreChanged = board::refresh))
|
viewModel { ShareViewModel(core, onStoreChanged = board::refresh) }
|
||||||
|
|
||||||
val update: UpdateViewModel = viewModel(factory = UpdateViewModel.factory(core, context))
|
val update: UpdateViewModel = viewModel { UpdateViewModel(core, context.applicationContext) }
|
||||||
val settings = remember(context) { SyncSettings(context) }
|
val settings = remember(context) { SyncSettings(context) }
|
||||||
var automatic by remember { mutableStateOf(settings.automatic) }
|
var automatic by remember { mutableStateOf(settings.automatic) }
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
|
||||||
import com.fabledsword.inkwell.core.Inkwell
|
import com.fabledsword.inkwell.core.Inkwell
|
||||||
import com.fabledsword.inkwell.core.Label
|
import com.fabledsword.inkwell.core.Label
|
||||||
import com.fabledsword.inkwell.core.Note
|
import com.fabledsword.inkwell.core.Note
|
||||||
@@ -660,13 +657,6 @@ class BoardViewModel(
|
|||||||
|
|
||||||
/** The board and the Archive together — everything not in Trash. */
|
/** The board and the Archive together — everything not in Trash. */
|
||||||
private const val VIEW_ALL = "all"
|
private const val VIEW_ALL = "all"
|
||||||
|
|
||||||
fun factory(
|
|
||||||
core: Inkwell,
|
|
||||||
readFile: (Uri) -> PickedFile,
|
|
||||||
onRemindersChanged: () -> Unit,
|
|
||||||
): ViewModelProvider.Factory =
|
|
||||||
viewModelFactory { initializer { BoardViewModel(core, onRemindersChanged, readFile) } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
|
||||||
import com.fabledsword.inkwell.core.CoreException
|
import com.fabledsword.inkwell.core.CoreException
|
||||||
import com.fabledsword.inkwell.core.Inkwell
|
import com.fabledsword.inkwell.core.Inkwell
|
||||||
import com.fabledsword.inkwell.core.Member
|
import com.fabledsword.inkwell.core.Member
|
||||||
@@ -132,11 +129,4 @@ class ShareViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun factory(
|
|
||||||
core: Inkwell,
|
|
||||||
onStoreChanged: () -> Unit,
|
|
||||||
): ViewModelProvider.Factory = viewModelFactory { initializer { ShareViewModel(core, onStoreChanged) } }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
|
||||||
import com.fabledsword.inkwell.core.Compatibility
|
import com.fabledsword.inkwell.core.Compatibility
|
||||||
import com.fabledsword.inkwell.core.Inkwell
|
import com.fabledsword.inkwell.core.Inkwell
|
||||||
import com.fabledsword.inkwell.core.ProbeResult
|
import com.fabledsword.inkwell.core.ProbeResult
|
||||||
@@ -315,13 +312,6 @@ class SyncViewModel(
|
|||||||
fun dismissSyncError() {
|
fun dismissSyncError() {
|
||||||
state = state.copy(syncError = null)
|
state = state.copy(syncError = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun factory(
|
|
||||||
core: Inkwell,
|
|
||||||
onStoreChanged: () -> Unit,
|
|
||||||
): ViewModelProvider.Factory = viewModelFactory { initializer { SyncViewModel(core, onStoreChanged) } }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
|
||||||
import com.fabledsword.inkwell.core.Inkwell
|
import com.fabledsword.inkwell.core.Inkwell
|
||||||
import com.fabledsword.inkwell.core.Label
|
import com.fabledsword.inkwell.core.Label
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -163,11 +160,4 @@ class TagsViewModel(
|
|||||||
onStoreChanged()
|
onStoreChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun factory(
|
|
||||||
core: Inkwell,
|
|
||||||
onStoreChanged: () -> Unit,
|
|
||||||
): ViewModelProvider.Factory = viewModelFactory { initializer { TagsViewModel(core, onStoreChanged) } }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
|
||||||
import com.fabledsword.inkwell.AppUpdate
|
import com.fabledsword.inkwell.AppUpdate
|
||||||
import com.fabledsword.inkwell.UpdateOutcome
|
import com.fabledsword.inkwell.UpdateOutcome
|
||||||
import com.fabledsword.inkwell.core.ClientUpdate
|
import com.fabledsword.inkwell.core.ClientUpdate
|
||||||
@@ -214,14 +211,6 @@ class UpdateViewModel(
|
|||||||
fun dismissError() {
|
fun dismissError() {
|
||||||
state = state.copy(error = null)
|
state = state.copy(error = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun factory(
|
|
||||||
core: Inkwell,
|
|
||||||
context: Context,
|
|
||||||
): ViewModelProvider.Factory =
|
|
||||||
viewModelFactory { initializer { UpdateViewModel(core, context.applicationContext) } }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val FALLBACK = "The update couldn't be checked."
|
private const val FALLBACK = "The update couldn't be checked."
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from inkwell.app import create_app
|
||||||
from inkwell.config import Config
|
from inkwell.config import Config
|
||||||
|
|
||||||
|
|
||||||
@@ -10,3 +11,9 @@ def _isolated_data_dir(tmp_path, monkeypatch):
|
|||||||
signing key from env-or-random, not a file), but this stays defensive."""
|
signing key from env-or-random, not a file), but this stays defensive."""
|
||||||
monkeypatch.setattr(Config, "DATA_DIR", str(tmp_path / "data"))
|
monkeypatch.setattr(Config, "DATA_DIR", str(tmp_path / "data"))
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app():
|
||||||
|
"""A fresh app for a test that drives routes through `app.test_client()`."""
|
||||||
|
return create_app()
|
||||||
|
|||||||
@@ -1,13 +1,3 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from inkwell.app import create_app
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_settings_requires_auth(app):
|
async def test_settings_requires_auth(app):
|
||||||
# require_admin returns 401 before any DB access when unauthenticated.
|
# require_admin returns 401 before any DB access when unauthenticated.
|
||||||
client = app.test_client()
|
client = app.test_client()
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from inkwell.app import create_app
|
from inkwell.app import create_app
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_health_ok(app):
|
async def test_health_ok(app):
|
||||||
client = app.test_client()
|
client = app.test_client()
|
||||||
resp = await client.get("/api/health")
|
resp = await client.get("/api/health")
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from inkwell import client_dist
|
from inkwell import client_dist
|
||||||
from inkwell.app import create_app
|
|
||||||
from inkwell.client_dist import (
|
from inkwell.client_dist import (
|
||||||
BY_ID,
|
BY_ID,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
@@ -54,11 +53,6 @@ def _empty_baked_client(tmp_path, monkeypatch):
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
def place(
|
def place(
|
||||||
platform_id: str = "android",
|
platform_id: str = "android",
|
||||||
payload: bytes = PAYLOAD,
|
payload: bytes = PAYLOAD,
|
||||||
|
|||||||
@@ -1,13 +1,3 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from inkwell.app import create_app
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_create_device_requires_auth(app):
|
async def test_create_device_requires_auth(app):
|
||||||
# No session cookie and no bearer header → 401 before any DB access.
|
# No session cookie and no bearer header → 401 before any DB access.
|
||||||
client = app.test_client()
|
client = app.test_client()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from inkwell import installer, settings
|
from inkwell import installer, settings
|
||||||
from inkwell.app import create_app
|
|
||||||
from inkwell.installer import PLACEHOLDER, SOURCE_SCRIPT, render, server_address
|
from inkwell.installer import PLACEHOLDER, SOURCE_SCRIPT, render, server_address
|
||||||
|
|
||||||
# DB-free, like test_client_dist: the route reads `public_url` from the live cache
|
# DB-free, like test_client_dist: the route reads `public_url` from the live cache
|
||||||
@@ -17,11 +16,6 @@ def _defaults():
|
|||||||
settings.reset_live()
|
settings.reset_live()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
def test_the_published_script_carries_the_line_the_server_rewrites_exactly_once():
|
def test_the_published_script_carries_the_line_the_server_rewrites_exactly_once():
|
||||||
"""The forge-served script and the server-served one are the same file; this is
|
"""The forge-served script and the server-served one are the same file; this is
|
||||||
the one line that differs. Edit it and the server stops serving rather than
|
the one line that differs. Edit it and the server stops serving rather than
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from inkwell.app import create_app
|
|
||||||
from inkwell.models.label import Label
|
from inkwell.models.label import Label
|
||||||
from inkwell.serialize import serialize_label
|
from inkwell.serialize import serialize_label
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
def test_serialize_label_shape():
|
def test_serialize_label_shape():
|
||||||
# The base {id, name, color} shape the labels API and (S4) sync deltas both build on.
|
# The base {id, name, color} shape the labels API and (S4) sync deltas both build on.
|
||||||
lid = uuid.uuid4()
|
lid = uuid.uuid4()
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from datetime import datetime, timezone
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from inkwell.app import create_app
|
|
||||||
from inkwell.common import coerce_bool, parse_dt
|
from inkwell.common import coerce_bool, parse_dt
|
||||||
from inkwell.colors import NOTE_COLORS
|
from inkwell.colors import NOTE_COLORS
|
||||||
from inkwell.models.note import Note
|
from inkwell.models.note import Note
|
||||||
@@ -34,11 +33,6 @@ from inkwell.notes.recurrence import next_occurrence, normalize_recurrence
|
|||||||
from inkwell.notes.tags import parse_tags, split_body_tags
|
from inkwell.notes.tags import parse_tags, split_body_tags
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
def test_all_note_routes_registered(app):
|
def test_all_note_routes_registered(app):
|
||||||
# Guards the notes package split: every route handler must still be attached to the
|
# Guards the notes package split: every route handler must still be attached to the
|
||||||
# blueprint. A route whose module isn't imported by notes/__init__ would silently
|
# blueprint. A route whose module isn't imported by notes/__init__ would silently
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import pytest
|
|||||||
|
|
||||||
from inkwell import ratelimit
|
from inkwell import ratelimit
|
||||||
from inkwell.settings import live
|
from inkwell.settings import live
|
||||||
from inkwell.app import create_app
|
|
||||||
from inkwell.ratelimit import SlidingWindow
|
from inkwell.ratelimit import SlidingWindow
|
||||||
|
|
||||||
|
|
||||||
@@ -28,11 +27,6 @@ def _clean_counters():
|
|||||||
ratelimit.reset_all()
|
ratelimit.reset_all()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
def test_under_the_limit_is_not_blocked():
|
def test_under_the_limit_is_not_blocked():
|
||||||
w = window(3, 60)
|
w = window(3, 60)
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
|
|||||||
@@ -4,14 +4,6 @@ Asserted on /api/health because it is the one route that needs no database and n
|
|||||||
session — the headers are set in an after_request hook, so any response proves the
|
session — the headers are set in an after_request hook, so any response proves the
|
||||||
hook, and this suite has no Postgres.
|
hook, and this suite has no Postgres.
|
||||||
"""
|
"""
|
||||||
import pytest
|
|
||||||
|
|
||||||
from inkwell.app import create_app
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_content_security_policy_locks_scripts_to_self(app):
|
async def test_content_security_policy_locks_scripts_to_self(app):
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from inkwell.app import create_app
|
|
||||||
from inkwell.sync import (
|
from inkwell.sync import (
|
||||||
DEFAULT_LIMIT,
|
DEFAULT_LIMIT,
|
||||||
MAX_LIMIT,
|
MAX_LIMIT,
|
||||||
@@ -17,11 +14,6 @@ from inkwell.sync import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
async def test_changes_requires_auth(app):
|
async def test_changes_requires_auth(app):
|
||||||
client = app.test_client()
|
client = app.test_client()
|
||||||
resp = await client.get("/api/sync/changes")
|
resp = await client.get("/api/sync/changes")
|
||||||
|
|||||||
@@ -2,15 +2,9 @@ import ipaddress
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from inkwell.app import create_app
|
|
||||||
from inkwell.unfurl import UnfurlError, extract_preview, is_public_ip, validate_url
|
from inkwell.unfurl import UnfurlError, extract_preview, is_public_ip, validate_url
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def app():
|
|
||||||
return create_app()
|
|
||||||
|
|
||||||
|
|
||||||
def test_is_public_ip_blocks_internal_ranges():
|
def test_is_public_ip_blocks_internal_ranges():
|
||||||
assert is_public_ip(ipaddress.ip_address("8.8.8.8"))
|
assert is_public_ip(ipaddress.ip_address("8.8.8.8"))
|
||||||
assert is_public_ip(ipaddress.ip_address("2606:4700:4700::1111"))
|
assert is_public_ip(ipaddress.ip_address("2606:4700:4700::1111"))
|
||||||
|
|||||||
Reference in New Issue
Block a user