Review and harden follow-user: kill mutation survivors

Add unit test pinning FollowQuery._nextString exclusive upper bound and
empty-string/non-string validation-rejection cases for the follow use
cases, killing the remaining mutation survivors. Update mutation manifests
and add the follow-user architectural review report.

By architect.
This commit is contained in:
Chris Troutner
2026-08-27 11:23:01 -07:00
parent b99d881205
commit bb7b5a6cfd
15 changed files with 116 additions and 2 deletions
@@ -1,3 +1,7 @@
# acceptance-mutation-manifest-begin
# {"version":1,"tested_at":"2026-08-27T18:18:35.249787308Z","feature_name":"Follow User","feature_path":"/home/trout/work/psf-memo/.worktrees/architect/psf-memo-client/specs/follow-user.feature","background_hash":"e1d5f81f1ed083ac6934c429ca3cb4a0f8d4dac44c2eaa45c0960920bde2c017","implementation_hash":"unknown","scenarios":[]}
# acceptance-mutation-manifest-end
# Scenarios: Follow User - 1, Follow User - 2, Follow User - 3, Follow User - 4, Follow User - 5
#
# The follow/unfollow OP_RETURN payload is the followee's 20-byte hash160.
+4
View File
@@ -107,3 +107,7 @@ class MemoDb {
}
export default MemoDb
// mutate4javascript-manifest-begin
// {"version":1,"tested_at":"2026-08-27T17:52:51.766Z","module_hash":"3f694a85efbd3d3d6fb3d24c8261166d36113990091680e5d11485bef973d6c9","functions":[{"id":"func/MemoDb.constructor","name":"MemoDb.constructor","line":9,"end_line":11,"hash":"188825ae5983840d4e1a4df182966ef012297c336ea3b763c87ea1c384654279"},{"id":"func/MemoDb.getRecentProfiles","name":"MemoDb.getRecentProfiles","line":13,"end_line":15,"hash":"cb819c72881b70f719f964c607f64be70b40377297c738897eee495bcf6dff4d"},{"id":"func/MemoDb.getRecentPosts","name":"MemoDb.getRecentPosts","line":17,"end_line":19,"hash":"7c0fd29c2fdc67a27a05a190e2e4294a698a1fe5223d8017ca82c4f890c2d094"},{"id":"func/MemoDb.getProfile","name":"MemoDb.getProfile","line":21,"end_line":23,"hash":"6a250c7205799b40dbe97b8f17ddaee6eace8cb435b0561d9ff8f5eac93d2b20"},{"id":"func/MemoDb.getProfilePic","name":"MemoDb.getProfilePic","line":25,"end_line":27,"hash":"d61dd5aebce32ef28b69ad56f2f7108d6b5f333a4d94cd83f05f01a09fa8f1d7"},{"id":"func/MemoDb.getName","name":"MemoDb.getName","line":29,"end_line":31,"hash":"3f841735b93180c6aeeb14a563bb6d4eb0ca5d395fb960267c8896b06c1e85f3"},{"id":"func/MemoDb.getFollowState","name":"MemoDb.getFollowState","line":33,"end_line":49,"hash":"d62eb85c742b40f2d319230c6ef59e21738063d31bccf1f37ebdc52c20bac994"},{"id":"func/MemoDb.getRecent","name":"MemoDb.getRecent","line":52,"end_line":63,"hash":"1565fd17dced45fbf1b6d6fd6b8003521129bbd88046ff52a4eb61c17862530d"},{"id":"func/MemoDb.getLevelResource","name":"MemoDb.getLevelResource","line":66,"end_line":79,"hash":"c2fb7338918b1e69aa8a14f65ae687532c843f0641f04074947731691001e4ea"},{"id":"func/MemoDb.getPostsByAddr","name":"MemoDb.getPostsByAddr","line":81,"end_line":93,"hash":"c39322f75bec244cffd3b66614ae2814040ac428cf39af53b04d3fcae4d59b19"},{"id":"func/MemoDb.getPostThread","name":"MemoDb.getPostThread","line":95,"end_line":106,"hash":"ed5ef157b457f8d984ff6a9488a4dccf7a37b0ae65adb40005df90508426661f"}]}
// mutate4javascript-manifest-end
@@ -114,3 +114,7 @@ MemoFollow.MEMO_UNFOLLOW_PREFIX = MEMO_UNFOLLOW_PREFIX
MemoFollow.PK_HASH_LENGTH = PK_HASH_LENGTH
module.exports = MemoFollow
// mutate4javascript-manifest-begin
// {"version":1,"tested_at":"2026-08-27T17:51:36.253Z","module_hash":"fc0d7db2a5f7872ae0d9228a19c7314b4f46f7574c53b2a168a62b986c43a31e","functions":[{"id":"func/MemoFollow.constructor","name":"MemoFollow.constructor","line":35,"end_line":38,"hash":"9407b43605444074011b1da595c9d53356352c72b0d847e00365d79ad705663a"},{"id":"func/MemoFollow.validate","name":"MemoFollow.validate","line":42,"end_line":57,"hash":"4154af442c36324ba147dff352c55610397fb430c9b62fbbe44728ab0e379179"},{"id":"func/MemoFollow.follow","name":"MemoFollow.follow","line":60,"end_line":62,"hash":"e24f69ef420d1ef625c2d03eae6014c824fdb35438a87e379fdc41747bbd321d"},{"id":"func/MemoFollow.unfollow","name":"MemoFollow.unfollow","line":65,"end_line":67,"hash":"48e93e85645bbcd767467afb807625b4adf6d6076f02fc841c91f58147bb44c7"},{"id":"func/MemoFollow._broadcastAction","name":"MemoFollow._broadcastAction","line":70,"end_line":88,"hash":"3720e2b6d78265383728bd7c5fcafeb018fad9c17a8bede12a6d4ffc3415cd3a"},{"id":"func/MemoFollow._toHash160","name":"MemoFollow._toHash160","line":92,"end_line":94,"hash":"3ed2ba4853b718a8316345c9f988ed3bed3ef647de3fd9761ee3c9e0bee86070"},{"id":"func/MemoFollow._ensureBchjs","name":"MemoFollow._ensureBchjs","line":96,"end_line":100,"hash":"92828d9038c9a4d367e80edd7f129291e434d57aae80b332081a66af9896a5d6"},{"id":"func/MemoFollow.reflect","name":"MemoFollow.reflect","line":104,"end_line":109,"hash":"af3f0ead1a9f51724e3c252122b3bf13c5323a3b9da244dee87a34815a90c94c"}]}
// mutate4javascript-manifest-end
+1 -1
View File
@@ -99,5 +99,5 @@ ProfilePage.PROFILE_PATH_PREFIX = PROFILE_PATH_PREFIX
module.exports = ProfilePage
// mutate4javascript-manifest-begin
// {"version":1,"tested_at":"2026-08-27T17:41:02.811Z","module_hash":"556ad5297067de3e39ddbb028a87d502cc0a9ab6525b1bba2c3d36c669207e1d","functions":[{"id":"func/ProfilePage.constructor","name":"ProfilePage.constructor","line":17,"end_line":25,"hash":"5e8bd826d8059e2d86aa3064c7b7d3041d5cf2fff7eb8942976bda600d2812a2"},{"id":"func/ProfilePage.load","name":"ProfilePage.load","line":27,"end_line":41,"hash":"e6c81ab6dbfda80903b134bdfea626d7e82e567780fd577d7a78e6dd44a22f37"},{"id":"func/ProfilePage._assertReady","name":"ProfilePage._assertReady","line":44,"end_line":51,"hash":"e575f98fa1b492b4c11ae5275459d9f9996df4c566b84c7cae91413c72a02f38"},{"id":"func/ProfilePage._loadFollowState","name":"ProfilePage._loadFollowState","line":55,"end_line":60,"hash":"65ff871041cab36b5e45acad5c8ca904c411af197f77dbb528e31ac5666668b3"},{"id":"func/ProfilePage.isOwnProfile","name":"ProfilePage.isOwnProfile","line":62,"end_line":64,"hash":"4d149bfe7b183b5d38d496c0a8126d22bd9a919e684f375d66e2c8c1348dd773"},{"id":"func/ProfilePage.canFollow","name":"ProfilePage.canFollow","line":66,"end_line":68,"hash":"0bc2c0c17991d8f1a1d18fba29c901e42633b7cf2c2c8b5747a49e5832d7001d"},{"id":"func/ProfilePage.isFollowing","name":"ProfilePage.isFollowing","line":70,"end_line":72,"hash":"9fc0470db7ffea2da96d2dbbdceff55562e1f7e84377fbb0e8b60d53cc723b40"},{"id":"func/ProfilePage.follow","name":"ProfilePage.follow","line":74,"end_line":76,"hash":"7674b789a9d3c48e0f7a6e553613bac99b2f38449fcdd17faa3c6d7cd2773bdd"},{"id":"func/ProfilePage.unfollow","name":"ProfilePage.unfollow","line":78,"end_line":80,"hash":"69d278b09da1f284be6adacb7264f9c06ac71c42f8e8f1baf9e34d5b24a891fb"},{"id":"func/ProfilePage._setFollowState","name":"ProfilePage._setFollowState","line":83,"end_line":90,"hash":"8eda1a436468b17327e8e20df4f84bf0263591f320c494f2c74c1b5be21b2ad8"},{"id":"func/ProfilePage.getPost","name":"ProfilePage.getPost","line":92,"end_line":94,"hash":"1a6ae1a02b0f79b5b62a2b2324a5f1edbd743bec004fe73cfef7970bead56885"}]}
// {"version":1,"tested_at":"2026-08-27T17:52:10.673Z","module_hash":"556ad5297067de3e39ddbb028a87d502cc0a9ab6525b1bba2c3d36c669207e1d","functions":[{"id":"func/ProfilePage.constructor","name":"ProfilePage.constructor","line":17,"end_line":25,"hash":"5e8bd826d8059e2d86aa3064c7b7d3041d5cf2fff7eb8942976bda600d2812a2"},{"id":"func/ProfilePage.load","name":"ProfilePage.load","line":27,"end_line":41,"hash":"e6c81ab6dbfda80903b134bdfea626d7e82e567780fd577d7a78e6dd44a22f37"},{"id":"func/ProfilePage._assertReady","name":"ProfilePage._assertReady","line":44,"end_line":51,"hash":"e575f98fa1b492b4c11ae5275459d9f9996df4c566b84c7cae91413c72a02f38"},{"id":"func/ProfilePage._loadFollowState","name":"ProfilePage._loadFollowState","line":55,"end_line":60,"hash":"65ff871041cab36b5e45acad5c8ca904c411af197f77dbb528e31ac5666668b3"},{"id":"func/ProfilePage.isOwnProfile","name":"ProfilePage.isOwnProfile","line":62,"end_line":64,"hash":"4d149bfe7b183b5d38d496c0a8126d22bd9a919e684f375d66e2c8c1348dd773"},{"id":"func/ProfilePage.canFollow","name":"ProfilePage.canFollow","line":66,"end_line":68,"hash":"0bc2c0c17991d8f1a1d18fba29c901e42633b7cf2c2c8b5747a49e5832d7001d"},{"id":"func/ProfilePage.isFollowing","name":"ProfilePage.isFollowing","line":70,"end_line":72,"hash":"9fc0470db7ffea2da96d2dbbdceff55562e1f7e84377fbb0e8b60d53cc723b40"},{"id":"func/ProfilePage.follow","name":"ProfilePage.follow","line":74,"end_line":76,"hash":"7674b789a9d3c48e0f7a6e553613bac99b2f38449fcdd17faa3c6d7cd2773bdd"},{"id":"func/ProfilePage.unfollow","name":"ProfilePage.unfollow","line":78,"end_line":80,"hash":"69d278b09da1f284be6adacb7264f9c06ac71c42f8e8f1baf9e34d5b24a891fb"},{"id":"func/ProfilePage._setFollowState","name":"ProfilePage._setFollowState","line":83,"end_line":90,"hash":"8eda1a436468b17327e8e20df4f84bf0263591f320c494f2c74c1b5be21b2ad8"},{"id":"func/ProfilePage.getPost","name":"ProfilePage.getPost","line":92,"end_line":94,"hash":"1a6ae1a02b0f79b5b62a2b2324a5f1edbd743bec004fe73cfef7970bead56885"}]}
// mutate4javascript-manifest-end
+1 -1
View File
@@ -65,5 +65,5 @@ class Profiles {
module.exports = Profiles
// mutate4javascript-manifest-begin
// {"version":1,"tested_at":"2026-08-27T15:05:55.871Z","module_hash":"7c75650f72fefd5e9f8fea638c1d9ca9fad9eeebabe16e62fed4f5e828e39248","functions":[{"id":"func/Profiles.constructor","name":"Profiles.constructor","line":13,"end_line":16,"hash":"8f7409de885759ce97767c69097a353ec557bd5212eef76ead0388174389ff24"},{"id":"func/Profiles.setName","name":"Profiles.setName","line":18,"end_line":21,"hash":"5a36c6e237798608de0bedd8744b75000c0eec5c0a6a64c870a25bcdf20aed21"},{"id":"func/Profiles.getName","name":"Profiles.getName","line":23,"end_line":26,"hash":"2fcb9d84687ea0f3b24f3e34c0a72eda55a4e869b87e08a7f4551321a4166198"},{"id":"func/Profiles.setBio","name":"Profiles.setBio","line":28,"end_line":31,"hash":"3825665ead1ba9bb217694c45a17bb2e11c78ffa053e5e90111a4b9208183b56"},{"id":"func/Profiles.getBio","name":"Profiles.getBio","line":33,"end_line":36,"hash":"2e9708249db4a0e4fa642cbe52e6216144ec91283281c61dee2ff3cc3d8f1572"}]}
// {"version":1,"tested_at":"2026-08-27T17:52:37.432Z","module_hash":"f729d4125eea29c45c4a0d7bd691a5f590512922f2ba78c85756643638fb0859","functions":[{"id":"func/Profiles.constructor","name":"Profiles.constructor","line":13,"end_line":18,"hash":"574f0b3772f783646c283c6ddc4d5ff200ca1f9918ab1fa668205003f885d4d3"},{"id":"func/Profiles.setName","name":"Profiles.setName","line":20,"end_line":23,"hash":"5a36c6e237798608de0bedd8744b75000c0eec5c0a6a64c870a25bcdf20aed21"},{"id":"func/Profiles.getName","name":"Profiles.getName","line":25,"end_line":28,"hash":"2fcb9d84687ea0f3b24f3e34c0a72eda55a4e869b87e08a7f4551321a4166198"},{"id":"func/Profiles.setBio","name":"Profiles.setBio","line":30,"end_line":33,"hash":"3825665ead1ba9bb217694c45a17bb2e11c78ffa053e5e90111a4b9208183b56"},{"id":"func/Profiles.getBio","name":"Profiles.getBio","line":35,"end_line":38,"hash":"2e9708249db4a0e4fa642cbe52e6216144ec91283281c61dee2ff3cc3d8f1572"},{"id":"func/Profiles.setAvatarUrl","name":"Profiles.setAvatarUrl","line":40,"end_line":43,"hash":"e4ccb0164fee4ab6cc3a1290fb4bf85e4a79f2227ca72872b1f10bc616926fd9"},{"id":"func/Profiles.getAvatarUrl","name":"Profiles.getAvatarUrl","line":45,"end_line":48,"hash":"0aa58dc15fd519e553136d04d1954e18084f79a6cd9349dc89b1136cf0a22a2b"},{"id":"func/Profiles.setFollowState","name":"Profiles.setFollowState","line":51,"end_line":57,"hash":"aa527936b6ca92a502e93e8241803318700c528e05f68ff7fb6fac1d410c197c"},{"id":"func/Profiles.getFollowState","name":"Profiles.getFollowState","line":59,"end_line":62,"hash":"299a44adb289026bf3b89a21947fc23bf05f5a40f07abb7f1bbd311c0479f562"}]}
// mutate4javascript-manifest-end