umap-project 2.6.2__py3-none-any.whl → 2.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of umap-project might be problematic. Click here for more details.
- umap/__init__.py +1 -1
- umap/admin.py +64 -1
- umap/asgi.py +15 -0
- umap/context_processors.py +1 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +96 -92
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +19 -18
- umap/locale/en/LC_MESSAGES/django.po +47 -43
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +134 -128
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +51 -47
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +64 -60
- umap/management/commands/clean_tilelayer.py +152 -0
- umap/management/commands/purge_purgatory.py +28 -0
- umap/models.py +27 -2
- umap/settings/base.py +3 -1
- umap/static/umap/base.css +4 -4
- umap/static/umap/css/contextmenu.css +6 -1
- umap/static/umap/css/icon.css +7 -2
- umap/static/umap/css/importers.css +4 -0
- umap/static/umap/img/16-white.svg +9 -2
- umap/static/umap/img/16.svg +1 -181
- umap/static/umap/img/24-white.svg +1 -0
- umap/static/umap/img/24.svg +1 -0
- umap/static/umap/img/importers/cadastrefr.svg +23 -0
- umap/static/umap/img/source/16-white.svg +10 -3
- umap/static/umap/img/source/16.svg +753 -197
- umap/static/umap/img/source/24-white.svg +3 -2
- umap/static/umap/img/source/24.svg +3 -2
- umap/static/umap/js/modules/autocomplete.js +7 -3
- umap/static/umap/js/modules/browser.js +55 -2
- umap/static/umap/js/modules/caption.js +16 -5
- umap/static/umap/js/modules/data/features.js +183 -8
- umap/static/umap/js/modules/data/layer.js +57 -40
- umap/static/umap/js/modules/formatter.js +3 -2
- umap/static/umap/js/modules/global.js +2 -0
- umap/static/umap/js/modules/importer.js +3 -0
- umap/static/umap/js/modules/importers/cadastrefr.js +62 -0
- umap/static/umap/js/modules/importers/communesfr.js +15 -3
- umap/static/umap/js/modules/permissions.js +123 -93
- umap/static/umap/js/modules/rendering/layers/classified.js +2 -0
- umap/static/umap/js/modules/rendering/ui.js +60 -213
- umap/static/umap/js/modules/share.js +1 -3
- umap/static/umap/js/modules/slideshow.js +1 -1
- umap/static/umap/js/modules/sync/engine.js +371 -14
- umap/static/umap/js/modules/sync/hlc.js +106 -0
- umap/static/umap/js/modules/sync/updaters.js +18 -6
- umap/static/umap/js/modules/sync/websocket.js +1 -1
- umap/static/umap/js/modules/tableeditor.js +1 -1
- umap/static/umap/js/modules/ui/base.js +2 -2
- umap/static/umap/js/modules/ui/contextmenu.js +51 -18
- umap/static/umap/js/modules/urls.js +5 -1
- umap/static/umap/js/modules/utils.js +28 -4
- umap/static/umap/js/umap.controls.js +76 -55
- umap/static/umap/js/umap.core.js +3 -3
- umap/static/umap/js/umap.forms.js +3 -1
- umap/static/umap/js/umap.js +115 -124
- umap/static/umap/locale/am_ET.js +2 -2
- umap/static/umap/locale/am_ET.json +2 -2
- umap/static/umap/locale/ar.js +2 -2
- umap/static/umap/locale/ar.json +2 -2
- umap/static/umap/locale/ast.js +2 -2
- umap/static/umap/locale/ast.json +2 -2
- umap/static/umap/locale/bg.js +2 -2
- umap/static/umap/locale/bg.json +2 -2
- umap/static/umap/locale/br.js +13 -4
- umap/static/umap/locale/br.json +13 -4
- umap/static/umap/locale/ca.js +30 -17
- umap/static/umap/locale/ca.json +30 -17
- umap/static/umap/locale/cs_CZ.js +89 -80
- umap/static/umap/locale/cs_CZ.json +89 -80
- umap/static/umap/locale/da.js +2 -2
- umap/static/umap/locale/da.json +2 -2
- umap/static/umap/locale/de.js +17 -8
- umap/static/umap/locale/de.json +17 -8
- umap/static/umap/locale/el.js +2 -2
- umap/static/umap/locale/el.json +2 -2
- umap/static/umap/locale/en.js +15 -4
- umap/static/umap/locale/en.json +15 -4
- umap/static/umap/locale/en_US.json +2 -2
- umap/static/umap/locale/es.js +338 -325
- umap/static/umap/locale/es.json +338 -325
- umap/static/umap/locale/et.js +2 -2
- umap/static/umap/locale/et.json +2 -2
- umap/static/umap/locale/eu.js +11 -4
- umap/static/umap/locale/eu.json +11 -4
- umap/static/umap/locale/fa_IR.js +11 -4
- umap/static/umap/locale/fa_IR.json +11 -4
- umap/static/umap/locale/fi.js +2 -2
- umap/static/umap/locale/fi.json +2 -2
- umap/static/umap/locale/fr.js +15 -4
- umap/static/umap/locale/fr.json +15 -4
- umap/static/umap/locale/gl.js +2 -2
- umap/static/umap/locale/gl.json +2 -2
- umap/static/umap/locale/he.js +2 -2
- umap/static/umap/locale/he.json +2 -2
- umap/static/umap/locale/hr.js +2 -2
- umap/static/umap/locale/hr.json +2 -2
- umap/static/umap/locale/hu.js +12 -5
- umap/static/umap/locale/hu.json +12 -5
- umap/static/umap/locale/id.js +2 -2
- umap/static/umap/locale/id.json +2 -2
- umap/static/umap/locale/is.js +2 -2
- umap/static/umap/locale/is.json +2 -2
- umap/static/umap/locale/it.js +2 -2
- umap/static/umap/locale/it.json +2 -2
- umap/static/umap/locale/ja.js +2 -2
- umap/static/umap/locale/ja.json +2 -2
- umap/static/umap/locale/ko.js +2 -2
- umap/static/umap/locale/ko.json +2 -2
- umap/static/umap/locale/lt.js +2 -2
- umap/static/umap/locale/lt.json +2 -2
- umap/static/umap/locale/ms.js +2 -2
- umap/static/umap/locale/ms.json +2 -2
- umap/static/umap/locale/nl.js +2 -2
- umap/static/umap/locale/nl.json +2 -2
- umap/static/umap/locale/no.js +2 -2
- umap/static/umap/locale/no.json +2 -2
- umap/static/umap/locale/pl.js +2 -2
- umap/static/umap/locale/pl.json +2 -2
- umap/static/umap/locale/pl_PL.json +2 -2
- umap/static/umap/locale/pt.js +19 -10
- umap/static/umap/locale/pt.json +19 -10
- umap/static/umap/locale/pt_BR.js +2 -2
- umap/static/umap/locale/pt_BR.json +2 -2
- umap/static/umap/locale/pt_PT.js +13 -4
- umap/static/umap/locale/pt_PT.json +13 -4
- umap/static/umap/locale/ro.js +2 -2
- umap/static/umap/locale/ro.json +2 -2
- umap/static/umap/locale/ru.js +2 -2
- umap/static/umap/locale/ru.json +2 -2
- umap/static/umap/locale/si.js +2 -2
- umap/static/umap/locale/si.json +2 -2
- umap/static/umap/locale/sk_SK.js +2 -2
- umap/static/umap/locale/sk_SK.json +2 -2
- umap/static/umap/locale/sl.js +2 -2
- umap/static/umap/locale/sl.json +2 -2
- umap/static/umap/locale/sr.js +2 -2
- umap/static/umap/locale/sr.json +2 -2
- umap/static/umap/locale/sv.js +2 -2
- umap/static/umap/locale/sv.json +2 -2
- umap/static/umap/locale/th_TH.js +2 -2
- umap/static/umap/locale/th_TH.json +2 -2
- umap/static/umap/locale/tr.js +2 -2
- umap/static/umap/locale/tr.json +2 -2
- umap/static/umap/locale/uk_UA.js +2 -2
- umap/static/umap/locale/uk_UA.json +2 -2
- umap/static/umap/locale/vi.js +2 -2
- umap/static/umap/locale/vi.json +2 -2
- umap/static/umap/locale/vi_VN.json +2 -2
- umap/static/umap/locale/zh.js +2 -2
- umap/static/umap/locale/zh.json +2 -2
- umap/static/umap/locale/zh_CN.json +2 -2
- umap/static/umap/locale/zh_TW.Big5.json +2 -2
- umap/static/umap/locale/zh_TW.js +13 -4
- umap/static/umap/locale/zh_TW.json +13 -4
- umap/static/umap/map.css +44 -29
- umap/static/umap/unittests/hlc.js +165 -0
- umap/static/umap/unittests/sync.js +321 -15
- umap/static/umap/unittests/utils.js +47 -0
- umap/static/umap/vars.css +2 -1
- umap/static/umap/vendors/colorbrewer/colorbrewer.js +309 -317
- umap/static/umap/vendors/dompurify/purify.es.js +15 -16
- umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
- umap/static/umap/vendors/georsstogeojson/GeoRSSToGeoJSON.js +111 -80
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +2 -2
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +1 -1
- umap/static/umap/vendors/simple-statistics/simple-statistics.min.js +1 -1
- umap/static/umap/vendors/simple-statistics/simple-statistics.min.js.map +1 -1
- umap/templates/umap/css.html +0 -2
- umap/templates/umap/dashboard_menu.html +4 -2
- umap/templates/umap/js.html +0 -5
- umap/templates/umap/map_detail.html +2 -2
- umap/tests/fixtures/test_upload_data.csv +2 -2
- umap/tests/integration/test_anonymous_owned_map.py +1 -0
- umap/tests/integration/test_basics.py +1 -1
- umap/tests/integration/test_browser.py +69 -7
- umap/tests/integration/test_caption.py +3 -3
- umap/tests/integration/test_circles_layer.py +12 -0
- umap/tests/integration/test_cluster.py +53 -0
- umap/tests/integration/test_datalayer.py +2 -1
- umap/tests/integration/test_draw_polygon.py +17 -9
- umap/tests/integration/test_draw_polyline.py +84 -7
- umap/tests/integration/test_edit_datalayer.py +5 -8
- umap/tests/integration/test_edit_map.py +2 -2
- umap/tests/integration/test_edit_marker.py +1 -1
- umap/tests/integration/test_facets_browser.py +3 -3
- umap/tests/integration/test_import.py +1 -0
- umap/tests/integration/test_map.py +1 -0
- umap/tests/integration/test_owned_map.py +1 -1
- umap/tests/integration/test_view_marker.py +63 -0
- umap/tests/integration/test_view_polygon.py +12 -12
- umap/tests/integration/test_websocket_sync.py +65 -3
- umap/tests/test_clean_tilelayer.py +83 -0
- umap/tests/test_datalayer.py +24 -0
- umap/tests/test_map_views.py +20 -0
- umap/tests/test_purge_purgatory.py +25 -0
- umap/tests/test_websocket_server.py +22 -0
- umap/urls.py +5 -1
- umap/views.py +6 -3
- umap/websocket_server.py +130 -27
- {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/METADATA +18 -14
- {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/RECORD +209 -200
- umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.css +0 -1
- umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.js +0 -7
- {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/WHEEL +0 -0
- {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.6.2.dist-info → umap_project-2.7.0.dist-info}/licenses/LICENSE +0 -0
umap/static/umap/map.css
CHANGED
|
@@ -500,7 +500,9 @@ ul.photon-autocomplete {
|
|
|
500
500
|
}
|
|
501
501
|
.leaflet-container .leaflet-control-edit-save,
|
|
502
502
|
.leaflet-container .leaflet-control-edit-cancel,
|
|
503
|
-
.leaflet-container .leaflet-control-edit-disable
|
|
503
|
+
.leaflet-container .leaflet-control-edit-disable,
|
|
504
|
+
.leaflet-container .leaflet-control-connected-peers
|
|
505
|
+
{
|
|
504
506
|
display: block;
|
|
505
507
|
border: none;
|
|
506
508
|
font-size: 12px;
|
|
@@ -510,9 +512,17 @@ ul.photon-autocomplete {
|
|
|
510
512
|
line-height: 30px;
|
|
511
513
|
padding: 0 20px;
|
|
512
514
|
}
|
|
515
|
+
.leaflet-container .leaflet-control-connected-peers,
|
|
516
|
+
.dark [type="button"].leaflet-control-connected-peers:hover
|
|
517
|
+
{
|
|
518
|
+
background-color: var(--color-lightCyan);
|
|
519
|
+
color: var(--color-dark);
|
|
520
|
+
}
|
|
521
|
+
|
|
513
522
|
.leaflet-container .leaflet-control-edit-disable:before,
|
|
514
523
|
.leaflet-container .leaflet-control-edit-save:before,
|
|
515
|
-
.leaflet-container .leaflet-control-edit-cancel:before
|
|
524
|
+
.leaflet-container .leaflet-control-edit-cancel:before,
|
|
525
|
+
.leaflet-container .leaflet-control-connected-peers:before {
|
|
516
526
|
display: inline-block;
|
|
517
527
|
width: 19px;
|
|
518
528
|
height: 24px;
|
|
@@ -523,9 +533,15 @@ ul.photon-autocomplete {
|
|
|
523
533
|
content: ' ';
|
|
524
534
|
text-align: center;
|
|
525
535
|
}
|
|
536
|
+
|
|
537
|
+
.leaflet-container .leaflet-control-connected-peers:before {
|
|
538
|
+
background-image: url('./img/16.svg');
|
|
539
|
+
}
|
|
540
|
+
|
|
526
541
|
.leaflet-container .leaflet-control-edit-disable span,
|
|
527
542
|
.leaflet-container .leaflet-control-edit-save span,
|
|
528
|
-
.leaflet-container .leaflet-control-edit-cancel span
|
|
543
|
+
.leaflet-container .leaflet-control-edit-cancel span,
|
|
544
|
+
.leaflet-container .leaflet-control-connected-peers span{
|
|
529
545
|
margin-inline-start: 10px;
|
|
530
546
|
}
|
|
531
547
|
.leaflet-container .leaflet-control-edit-save:before {
|
|
@@ -534,8 +550,12 @@ ul.photon-autocomplete {
|
|
|
534
550
|
.leaflet-container .leaflet-control-edit-disable:before {
|
|
535
551
|
background-position: -50px -25px;
|
|
536
552
|
}
|
|
553
|
+
.leaflet-container .leaflet-control-connected-peers:before {
|
|
554
|
+
background-position: -2px -95px;
|
|
555
|
+
}
|
|
537
556
|
.leaflet-container .leaflet-control-edit-cancel,
|
|
538
|
-
.leaflet-container .leaflet-control-edit-disable
|
|
557
|
+
.leaflet-container .leaflet-control-edit-disable,
|
|
558
|
+
.leaflet-container .leaflet-control-connected-peers{
|
|
539
559
|
border: 0.5px solid rgba(153, 153, 153, 0.40);
|
|
540
560
|
}
|
|
541
561
|
.leaflet-container .leaflet-control-edit-cancel:hover,
|
|
@@ -592,17 +612,16 @@ ul.photon-autocomplete {
|
|
|
592
612
|
left: 0;
|
|
593
613
|
right: 0;
|
|
594
614
|
height: 46px;
|
|
595
|
-
background-color: var(--color-darkGray);
|
|
596
615
|
padding: 0 10px;
|
|
597
616
|
text-align: start;
|
|
598
617
|
line-height: var(--control-size);
|
|
599
618
|
cursor: auto;
|
|
600
619
|
border-bottom: 1px solid #222;
|
|
601
620
|
z-index: var(--zindex-panels);
|
|
602
|
-
opacity: 0.98;
|
|
603
|
-
color: #fff;
|
|
604
621
|
display: flex;
|
|
605
622
|
justify-content: space-between;
|
|
623
|
+
background-color: var(--background-color);
|
|
624
|
+
color: var(--text-color);
|
|
606
625
|
}
|
|
607
626
|
.umap-left-edit-toolbox,
|
|
608
627
|
.umap-right-edit-toolbox {
|
|
@@ -820,7 +839,6 @@ a.umap-control-caption,
|
|
|
820
839
|
.umap-browser .off .feature {
|
|
821
840
|
display: none;
|
|
822
841
|
}
|
|
823
|
-
.umap-facet-search .formbox,
|
|
824
842
|
.umap-browser .datalayer {
|
|
825
843
|
margin-bottom: 2px;
|
|
826
844
|
border-radius: 2px;
|
|
@@ -831,7 +849,7 @@ a.umap-control-caption,
|
|
|
831
849
|
.umap-browser.dark .datalayer ul {
|
|
832
850
|
border: 1px solid #232729;
|
|
833
851
|
}
|
|
834
|
-
.umap-browser h5
|
|
852
|
+
.umap-browser h5 {
|
|
835
853
|
margin-bottom: 0;
|
|
836
854
|
overflow: hidden;
|
|
837
855
|
padding-inline-start: 5px;
|
|
@@ -849,7 +867,7 @@ a.umap-control-caption,
|
|
|
849
867
|
.umap-browser h5 span {
|
|
850
868
|
margin-inline-start: 10px;
|
|
851
869
|
}
|
|
852
|
-
.umap-browser li
|
|
870
|
+
.umap-browser li {
|
|
853
871
|
padding: 2px 0;
|
|
854
872
|
white-space: nowrap;
|
|
855
873
|
overflow: hidden;
|
|
@@ -867,13 +885,15 @@ a.umap-control-caption,
|
|
|
867
885
|
-moz-box-sizing:border-box;
|
|
868
886
|
-webkit-box-sizing:border-box;
|
|
869
887
|
box-sizing: border-box;
|
|
870
|
-
background: none;
|
|
871
888
|
display: inline-block;
|
|
872
889
|
padding: 0;
|
|
873
890
|
width: 24px;
|
|
874
891
|
text-align: center;
|
|
875
892
|
margin-inline-start: 5px;
|
|
876
893
|
}
|
|
894
|
+
.umap-browser .marker .feature-color {
|
|
895
|
+
background: none;
|
|
896
|
+
}
|
|
877
897
|
.umap-browser.dark .datalayer .feature-color {
|
|
878
898
|
box-shadow: 0 0 2px 0 #999 inset;
|
|
879
899
|
}
|
|
@@ -884,18 +904,6 @@ a.umap-control-caption,
|
|
|
884
904
|
font-style: normal;
|
|
885
905
|
font-weight: bold;
|
|
886
906
|
}
|
|
887
|
-
.umap-browser .polygon .feature-color,
|
|
888
|
-
.umap-browser .polyline .feature-color {
|
|
889
|
-
box-shadow: 0 0 2px 0 black inset;
|
|
890
|
-
background-image: url('./img/24.svg');
|
|
891
|
-
background-size: 500%;
|
|
892
|
-
}
|
|
893
|
-
.umap-browser .polyline .feature-color {
|
|
894
|
-
background-position: -72px -23px;
|
|
895
|
-
}
|
|
896
|
-
.umap-browser .polygon .feature-color {
|
|
897
|
-
background-position: -48px -25px;
|
|
898
|
-
}
|
|
899
907
|
.umap-browser .datalayer-toggle-list {
|
|
900
908
|
float: inline-end;
|
|
901
909
|
margin-inline-end: 5px;
|
|
@@ -925,6 +933,16 @@ a.umap-control-caption,
|
|
|
925
933
|
.umap-caption .umap-map-author {
|
|
926
934
|
padding-inline-start: 31px;
|
|
927
935
|
}
|
|
936
|
+
.umap-browser .main-toolbox {
|
|
937
|
+
padding-left: 4px; /* Align with toolbox below */
|
|
938
|
+
border-top: 1px solid var(--color-mediumGray);
|
|
939
|
+
margin-top: var(--box-margin);
|
|
940
|
+
padding-top: 3px;
|
|
941
|
+
padding-bottom: 3px;
|
|
942
|
+
}
|
|
943
|
+
.umap-browser .main-toolbox i {
|
|
944
|
+
cursor: pointer;
|
|
945
|
+
}
|
|
928
946
|
|
|
929
947
|
|
|
930
948
|
/* ********************************* */
|
|
@@ -982,18 +1000,18 @@ a.umap-control-caption,
|
|
|
982
1000
|
vertical-align: middle;
|
|
983
1001
|
}
|
|
984
1002
|
|
|
985
|
-
.
|
|
1003
|
+
.caption-item {
|
|
986
1004
|
color: #555;
|
|
987
1005
|
padding: 6px 8px;
|
|
988
1006
|
box-shadow: 0 0 3px rgba(0,0,0,0.2);
|
|
989
1007
|
border-radius: 1px;
|
|
990
1008
|
}
|
|
991
|
-
.
|
|
1009
|
+
.caption-item ul {
|
|
992
1010
|
list-style-type: none;
|
|
993
1011
|
padding: 0;
|
|
994
1012
|
margin: 0;
|
|
995
1013
|
}
|
|
996
|
-
.
|
|
1014
|
+
.caption-item .circles-layer-legend {
|
|
997
1015
|
padding: var(--box-padding);
|
|
998
1016
|
}
|
|
999
1017
|
.circles-layer-legend li {
|
|
@@ -1499,9 +1517,6 @@ span.popup-icon {
|
|
|
1499
1517
|
.umap-main-edit-toolbox .umap-user {
|
|
1500
1518
|
margin-inline-end: 10px;
|
|
1501
1519
|
}
|
|
1502
|
-
.umap-main-edit-toolbox .umap-user:after {
|
|
1503
|
-
display: none;
|
|
1504
|
-
}
|
|
1505
1520
|
}
|
|
1506
1521
|
@media all and (max-width: 640px) {
|
|
1507
1522
|
.umap-main-edit-toolbox .umap-user {
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { describe, it } from 'mocha'
|
|
2
|
+
import sinon from 'sinon'
|
|
3
|
+
|
|
4
|
+
import pkg from 'chai'
|
|
5
|
+
const { expect } = pkg
|
|
6
|
+
|
|
7
|
+
import { HybridLogicalClock } from '../js/modules/sync/hlc.js'
|
|
8
|
+
|
|
9
|
+
describe('HybridLogicalClock', () => {
|
|
10
|
+
let clock
|
|
11
|
+
|
|
12
|
+
describe('#parse', () => {
|
|
13
|
+
it('should reject invalid values', () => {
|
|
14
|
+
clock = new HybridLogicalClock()
|
|
15
|
+
expect(() => clock.parse('invalid')).to.throw()
|
|
16
|
+
expect(() => clock.parse('123:456')).to.throw()
|
|
17
|
+
expect(() => clock.parse('123:456:789:000')).to.throw()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should parse correct values', () => {
|
|
21
|
+
clock = new HybridLogicalClock()
|
|
22
|
+
const result = clock.parse('1625097600000:42:abc-123')
|
|
23
|
+
expect(result).to.deep.equal({
|
|
24
|
+
walltime: '1625097600000',
|
|
25
|
+
nn: 42,
|
|
26
|
+
id: 'abc-123',
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should default to 0 for nn if none is provided', () => {
|
|
31
|
+
clock = new HybridLogicalClock()
|
|
32
|
+
const result = clock.parse('1625097600000::abc-123')
|
|
33
|
+
expect(result).to.deep.equal({
|
|
34
|
+
walltime: '1625097600000',
|
|
35
|
+
nn: 0,
|
|
36
|
+
id: 'abc-123',
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('#serialize', () => {
|
|
42
|
+
it('should correctly serialize the clock', () => {
|
|
43
|
+
clock = new HybridLogicalClock(1625097600000, 42, 'abc-123')
|
|
44
|
+
expect(clock.serialize()).to.equal('1625097600000:42:abc-123')
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('#tick', () => {
|
|
49
|
+
it('should increment walltime when current time is greater', () => {
|
|
50
|
+
const now = Date.now()
|
|
51
|
+
clock = new HybridLogicalClock(now - 1000, 0, 'test')
|
|
52
|
+
const result = clock.tick()
|
|
53
|
+
const parsed = clock.parse(result)
|
|
54
|
+
expect(parsed.walltime).to.be.at.least(now.toString())
|
|
55
|
+
expect(parsed.nn).to.equal(0)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('should increment nn when current time is not greater', () => {
|
|
59
|
+
const now = Date.now()
|
|
60
|
+
clock = new HybridLogicalClock(now, 5, 'test')
|
|
61
|
+
sinon.useFakeTimers(now)
|
|
62
|
+
const result = clock.tick()
|
|
63
|
+
const parsed = clock.parse(result)
|
|
64
|
+
expect(parsed.walltime).to.equal(now.toString())
|
|
65
|
+
expect(parsed.nn).to.equal(6)
|
|
66
|
+
sinon.restore()
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('#receive', () => {
|
|
71
|
+
it("should use current time when it's greater than both local and remote", () => {
|
|
72
|
+
const now = Date.now()
|
|
73
|
+
clock = new HybridLogicalClock(now - 1000, 0, 'local')
|
|
74
|
+
const result = clock.receive(`${now - 500}:0:remote`)
|
|
75
|
+
expect(result.walltime).to.be.at.least(now)
|
|
76
|
+
expect(result.nn).to.equal(0)
|
|
77
|
+
expect(result.id).to.equal('local')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should increment nn when local and remote times are equal', () => {
|
|
81
|
+
const now = Date.now()
|
|
82
|
+
// mock the clock to be the same time
|
|
83
|
+
sinon.useFakeTimers(now)
|
|
84
|
+
clock = new HybridLogicalClock(now, 5, 'local')
|
|
85
|
+
const result = clock.receive(`${now}:7:remote`)
|
|
86
|
+
expect(result.walltime).to.be.at.least(now)
|
|
87
|
+
expect(result.nn).to.equal(8)
|
|
88
|
+
expect(result.id).to.equal('local')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should use remote time and increment nn when remote time is greater', () => {
|
|
92
|
+
const now = Date.now()
|
|
93
|
+
clock = new HybridLogicalClock(now - 1000, 5, 'local')
|
|
94
|
+
const result = clock.receive(`${now}:7:remote`)
|
|
95
|
+
expect(result.walltime).to.be.least(now.toString())
|
|
96
|
+
expect(result.nn).to.equal(8)
|
|
97
|
+
expect(result.id).to.equal('local')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should increment local nn when local time is greater', () => {
|
|
101
|
+
const now = Date.now()
|
|
102
|
+
clock = new HybridLogicalClock(now, 5, 'local')
|
|
103
|
+
const result = clock.receive(`${now - 1000}:7:remote`)
|
|
104
|
+
expect(result.walltime).to.be.least(now)
|
|
105
|
+
if (result.walltime > now) {
|
|
106
|
+
expect(result.nn).to.equal(5)
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
expect(result.nn).to.equal(6)
|
|
110
|
+
}
|
|
111
|
+
expect(result.id).to.equal('local')
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should maintain causal order across multiple operations', () => {
|
|
116
|
+
const hlc = new HybridLogicalClock()
|
|
117
|
+
|
|
118
|
+
// Simulate a sequence of events
|
|
119
|
+
const event1 = hlc.tick()
|
|
120
|
+
|
|
121
|
+
// Simulate some time passing
|
|
122
|
+
const clock = sinon.useFakeTimers(Date.now() + 100)
|
|
123
|
+
|
|
124
|
+
const event2 = hlc.tick()
|
|
125
|
+
|
|
126
|
+
// Simulate receiving a message from another node
|
|
127
|
+
hlc.receive(`${Date.now() - 50}:5:remote-id`)
|
|
128
|
+
|
|
129
|
+
const event3 = hlc.tick()
|
|
130
|
+
|
|
131
|
+
// Advance time significantly
|
|
132
|
+
clock.tick(1000)
|
|
133
|
+
|
|
134
|
+
const event4 = hlc.tick()
|
|
135
|
+
|
|
136
|
+
// Clean up the fake timer
|
|
137
|
+
clock.restore()
|
|
138
|
+
|
|
139
|
+
// Parse all events
|
|
140
|
+
const parsedEvent1 = hlc.parse(event1)
|
|
141
|
+
const parsedEvent2 = hlc.parse(event2)
|
|
142
|
+
const parsedEvent3 = hlc.parse(event3)
|
|
143
|
+
const parsedEvent4 = hlc.parse(event4)
|
|
144
|
+
|
|
145
|
+
// Assertions to ensure causal order is maintained
|
|
146
|
+
expect(parsedEvent2.walltime).to.be.greaterThan(parsedEvent1.walltime)
|
|
147
|
+
expect(parsedEvent3.walltime).to.equal(parsedEvent2.walltime)
|
|
148
|
+
expect(parsedEvent3.nn).to.be.greaterThan(parsedEvent2.nn)
|
|
149
|
+
expect(parsedEvent4.walltime).to.be.greaterThan(parsedEvent3.walltime)
|
|
150
|
+
|
|
151
|
+
// Check that all events have the same id
|
|
152
|
+
const uniqueIds = new Set([
|
|
153
|
+
parsedEvent1.id,
|
|
154
|
+
parsedEvent2.id,
|
|
155
|
+
parsedEvent3.id,
|
|
156
|
+
parsedEvent4.id,
|
|
157
|
+
])
|
|
158
|
+
expect(uniqueIds.size).to.equal(1)
|
|
159
|
+
|
|
160
|
+
// Ensure we can compare events as strings and maintain the same order
|
|
161
|
+
const events = [event1, event2, event3, event4]
|
|
162
|
+
const sortedEvents = [...events].sort()
|
|
163
|
+
expect(sortedEvents).to.deep.equal(events)
|
|
164
|
+
})
|
|
165
|
+
})
|