Skip to content

Commit

Permalink
editoast, front: forbid zero-length paths
Browse files Browse the repository at this point in the history
- Editoast: treat core pathfinding responses with a length of zero as
  errors.
- Frontend, stdcm: add error notifications when the user enters the same
  origin and destination.
  • Loading branch information
Sh099078 committed Jan 22, 2025
1 parent 8d71c73 commit f94837f
Show file tree
Hide file tree
Showing 14 changed files with 102 additions and 8 deletions.
8 changes: 8 additions & 0 deletions editoast/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8435,6 +8435,14 @@ components:
- rolling_stock_not_found
rolling_stock_name:
type: string
- type: object
required:
- error_type
properties:
error_type:
type: string
enum:
- zero_length_path
PathfindingItem:
type: object
required:
Expand Down
1 change: 1 addition & 0 deletions editoast/src/core/pathfinding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ pub enum PathfindingInputError {
RollingStockNotFound {
rolling_stock_name: String,
},
ZeroLengthPath,
}

// Enum for not-found results and incompatible constraints
Expand Down
58 changes: 55 additions & 3 deletions editoast/src/views/path/pathfinding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,12 @@ pub enum PathfindingResult {
impl From<PathfindingCoreResult> for PathfindingResult {
fn from(core_result: PathfindingCoreResult) -> Self {
match core_result {
PathfindingCoreResult::Success(success) => PathfindingResult::Success(success),
PathfindingCoreResult::Success(success) => match success.length {
0 => PathfindingResult::Failure(PathfindingFailure::PathfindingInputError(
PathfindingInputError::ZeroLengthPath,
)),
_ => PathfindingResult::Success(success),
},
PathfindingCoreResult::NotFoundInBlocks {
track_section_ranges,
length,
Expand Down Expand Up @@ -456,6 +461,53 @@ pub mod tests {
use crate::views::path::pathfinding::PathfindingResult;
use crate::views::test_app::TestAppBuilder;

#[rstest]
async fn pathfinding_fails_when_core_responds_with_zero_length_path() {
let db_pool = DbConnectionPoolV2::for_tests();
let mut core = MockingClient::new();
core.stub("/v2/pathfinding/blocks")
.method(reqwest::Method::POST)
.response(StatusCode::OK)
.json(json!({
"blocks":[],
"routes": [],
"track_section_ranges": [],
"path_item_positions": [],
"length": 0,
"status": "success"
}))
.finish();
let app = TestAppBuilder::new()
.db_pool(db_pool.clone())
.core_client(core.into())
.build();
let small_infra = create_small_infra(&mut db_pool.get_ok()).await;

let request = app
.post(format!("/infra/{}/pathfinding/blocks", small_infra.id).as_str())
.json(&json!({
"path_items":[
{"trigram":"WS","secondary_code":"BV"},
{"trigram":"WS","secondary_code":"BV"}
],
"rolling_stock_is_thermal":true,
"rolling_stock_loading_gauge":"G1",
"rolling_stock_supported_electrifications":[],
"rolling_stock_supported_signaling_systems":["BAL","BAPR"],
"rolling_stock_maximum_speed":22.00,
"rolling_stock_length":26.00
}));

let pathfinding_result: PathfindingResult =
app.fetch(request).assert_status(StatusCode::OK).json_into();
assert_eq!(
pathfinding_result,
PathfindingResult::Failure(PathfindingFailure::PathfindingInputError(
PathfindingInputError::ZeroLengthPath,
))
);
}

#[rstest]
async fn pathfinding_with_invalid_path_items_returns_invalid_path_items() {
let app = TestAppBuilder::default_app();
Expand Down Expand Up @@ -560,7 +612,7 @@ pub mod tests {
"routes": [],
"track_section_ranges": [],
"path_item_positions": [],
"length": 0,
"length": 1,
"status": "success"
}))
.finish();
Expand Down Expand Up @@ -593,7 +645,7 @@ pub mod tests {
blocks: vec![],
routes: vec![],
track_section_ranges: vec![],
length: 0,
length: 1,
path_item_positions: vec![]
})
);
Expand Down
2 changes: 1 addition & 1 deletion editoast/src/views/stdcm_logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ mod tests {
blocks: vec![],
routes: vec![],
track_section_ranges: vec![],
length: 0,
length: 1,
path_item_positions: vec![0, 10],
}
}
Expand Down
2 changes: 1 addition & 1 deletion editoast/src/views/timetable/stdcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ mod tests {
blocks: vec![],
routes: vec![],
track_section_ranges: vec![],
length: 0,
length: 1,
path_item_positions: vec![0, 10],
}
}
Expand Down
2 changes: 1 addition & 1 deletion editoast/src/views/train_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ mod tests {
"routes": [],
"track_section_ranges": [],
"path_item_positions": [0,1,2,3],
"length": 0,
"length": 1,
"status": "success"
}))
.finish();
Expand Down
5 changes: 5 additions & 0 deletions front/public/locales/en/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
"WrongRailjsonVersionProvided": "Wrong railjson version provided"
}
},
"core": {
"pathfinding": {
"ZeroLengthPath": "Path length cannot be null."
}
},
"infra_state": {
"FetchError": "Error while fetching the infrastructure loading status"
},
Expand Down
3 changes: 2 additions & 1 deletion front/public/locales/en/stdcm.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@
"noResults": "No path found",
"noScheduledPoint": "The calculation requires either the origin or destination schedule.",
"pathfindingFailed": "No path have been found for these waypoints.",
"requestFailed": "Calculation error for last minute train path"
"requestFailed": "Calculation error for last minute train path",
"zeroLengthPath": "The path cannot have a null length."
},
"stdcmResults": "Results",
"stdcmSimulationReport": "Path simulation report",
Expand Down
5 changes: 5 additions & 0 deletions front/public/locales/fr/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
"WrongRailjsonVersionProvided": "Mauvaise version de railjson fournie"
}
},
"core": {
"pathfinding": {
"ZeroLengthPath": "La longueur d'un chemin ne peut pas être nulle."
}
},
"infra_state": {
"FetchError": "Erreur de récupération de l'état de chargement de l'infrastructure"
},
Expand Down
3 changes: 2 additions & 1 deletion front/public/locales/fr/stdcm.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@
"noResults": "Aucun sillon trouvé",
"noScheduledPoint": "Le calcul a besoin de l'horaire d'origine ou de celui de la destination.",
"pathfindingFailed": "Aucun chemin n'a été trouvé pour ces points de jalonnement.",
"requestFailed": "Erreur de calcul Sillon de dernière minute"
"requestFailed": "Erreur de calcul Sillon de dernière minute",
"zeroLengthPath": "Le chemin ne peut pas avoir de longueur nulle."
},
"stdcmResults": "Résultats",
"stdcmSimulationReport": "Fiche simulation",
Expand Down
10 changes: 10 additions & 0 deletions front/src/applications/stdcm/hooks/useStaticPathfinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ const useStaticPathfinding = (infra?: InfraWithState) => {
return;
}

// Don't run the pathfinding if the origin and destination are the same:
const origin = pathSteps.at(0)!;
const destination = pathSteps.at(-1)!;
if (
origin.location!.uic === destination.location!.uic &&
origin.location!.secondary_code === destination.location!.secondary_code
) {
return;
}

const payload = getPathfindingQuery({
infraId: infra.id,
rollingStock,
Expand Down
1 change: 1 addition & 0 deletions front/src/applications/stdcm/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export enum StdcmConfigErrorTypes {
PATHFINDING_FAILED = 'pathfindingFailed',
BOTH_POINT_SCHEDULED = 'bothPointAreScheduled',
NO_SCHEDULED_POINT = 'noScheduledPoint',
ZERO_LENGTH_PATH = 'zeroLengthPath',
}

export type StdcmConfigErrors = {
Expand Down
7 changes: 7 additions & 0 deletions front/src/applications/stdcm/utils/checkStdcmConfigErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ const checkStdcmConfigErrors = (
throw new Error('Last step can not be a via');
}

if (
origin.location!.uic === destination.location!.uic &&
origin.location!.secondary_code === destination.location!.secondary_code
) {
return { errorType: StdcmConfigErrorTypes.ZERO_LENGTH_PATH };
}

if (pathfindingStateError) {
return { errorType: StdcmConfigErrorTypes.PATHFINDING_FAILED };
}
Expand Down
3 changes: 3 additions & 0 deletions front/src/common/api/generatedEditoastApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2700,6 +2700,9 @@ export type PathfindingInputError =
| {
error_type: 'rolling_stock_not_found';
rolling_stock_name: string;
}
| {
error_type: 'zero_length_path';
};
export type OffsetRange = {
end: number;
Expand Down

0 comments on commit f94837f

Please sign in to comment.