Skip to content

Add comprehensive unit tests for Flickr API methods#150

Merged
beaufour merged 28 commits into
masterfrom
extend-testing
Jan 24, 2026
Merged

Add comprehensive unit tests for Flickr API methods#150
beaufour merged 28 commits into
masterfrom
extend-testing

Conversation

@beaufour

@beaufour beaufour commented Jan 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds extensive unit test coverage for Flickr API methods across all modules
  • Fixes 23 bugs discovered through testing (see below)
  • Refactors tests to use shared base class for common setUp and mock response handling
  • Adds api-docs JSON files for test fixtures

Bug Fixes

Critical Fixes (methods that were completely broken)

  1. Photo.getGalleries - Loop iterated over empty list, Person constructor missing id= keyword, and primary_photo incorrectly typed as Gallery instead of Photo
  2. Collection.getInfo - Loop iterated over wrong variable (photos instead of icon_photos), Person constructor missing id= keyword
  3. Collection.getTree / Person.getCollectionTree - Wrong data extraction path, missing default for set key causing KeyError on empty sets
  4. Group.addDiscussTopic - Missing self parameter (method was unusable)
  5. Group.Topic.Reply.delete - Missing return statement
  6. Group.Topic.Reply.edit - Method was entirely missing
  7. Photo.getSuggestions - Incorrect syntax (s.pop(Photo(...)) instead of assignment)
  8. _extract_activity_list - Wrong dict pop syntax (item.pop(["type"]) vs item.pop("type")), wrong variable name (events vs events_)

API Response Parsing Fixes

  1. Camera.Brand.getModels - Added _check_list wrapper for single-item responses
  2. Group.Topic.getReplies - Pagination info extracted from wrong level in response
  3. Photo.rotate - Wrong key for photo_id (_content vs text)
  4. Photo.getPeople - Missing _check_list, wrong format_result signature
  5. Photoset.addComment - Missing extraction of comment key from response
  6. Photoset.getComments - Missing .get() default causing KeyError, wrong variable (photo vs photoset)
  7. Place.parse_shapedata - Not handling single polyline (string vs list)
  8. Place.placesForBoundingBox / placesForContacts - Missing ** in Place constructor, not handling single place
  9. Tag.getListUserRaw - Not handling single tag response
  10. _extract_place_list - Not handling single place, not converting text to name

Constructor/Argument Fixes

  1. BlogService.postPhoto - Missing "photo" argument in _format_id call
  2. Group.getPoolContext - Missing ** in second Photo constructor
  3. Photo.setLicence - Missing logic to handle licence object vs ID

Boolean Conversion Fix

  1. Blog.needspassword - Changed from bool() to str_to_bool() (fixes bool("0") = True bug)
  2. Group.Topic/Reply author is_pro - Added default value to prevent KeyError

Test Coverage

Unit tests added for:

  • Activity, Camera, Contact, Gallery, Group, License, People API methods
  • Photo API methods (core, geo, comments, notes, suggestions, transform, upload)
  • Photoset, Place, Prefs, Reflection, Stats, Tags, Test, URLs API methods

Test plan

  • All unit tests pass with uv run pytest

🤖 Generated with Claude Code

beaufour and others added 28 commits January 24, 2026 14:16
Add test coverage for Batch 1 of the API test plan:
- Activity.userComments and Activity.userPhotos
- BlogService.getServices, BlogService.getList, Blog.postPhoto

Fix bugs discovered during testing:
- _extract_activity_list: fix item.pop(["type"]) -> item.pop("type")
- _extract_activity_list: fix events -> events_ variable name
- BlogService.postPhoto: add missing "photo" arg to _format_id()
- Blog.needspassword: add str_to_bool() to properly convert "0"/"1"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests added for Batch 2 (5 methods):
- flickr.cameras.getBrands (Camera.Brand.getList)
- flickr.cameras.getBrandModels (Camera.Brand.getModels)
- flickr.collections.getInfo (Collection.getInfo)
- flickr.collections.getTree (Collection.getTree, Person.getCollectionTree)
- flickr.commons.getInstitutions (CommonInstitution.getInstitutions)

Bug fixes in objects.py:
- Camera.Brand.getModels: Add _check_list() to handle single camera responses
- Collection.getInfo: Add token parameter, fix iteration over icon_photos
- Collection.getTree: Fix to access r["collections"]["collection"]
- Person.getCollectionTree: Same fix as Collection.getTree

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Test Contact.getList, Contact.getListRecentlyUploaded, and
Contact.getTaggingSuggestions using example API responses.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add test_favorites.py with 7 tests for Photo.addToFavorites,
  Person.getFavoriteContext, Photo.getFavoriteContext, Person.getFavorites,
  Person.getPublicFavorites, Person.removeFromFavorites, and
  Photo.removeFromFavorites

- Extract shared test utilities into test_utils.py:
  - xml_to_flickr_json(): Converts XML to JSON, handles boolean conversion
  - load_api_doc(): Loads API doc JSON files

- Update all test files to use shared utilities from test_utils.py

- Fix boolean handling: "0"/"1" strings are now converted to integers
  so the library's bool() converters work correctly

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests added for Gallery.addPhoto, Gallery.create, Gallery.editMedia,
Gallery.editPhoto, Gallery.editPhotos, Gallery.getInfo, Gallery.getPhotos,
Person.getGalleries, and Photo.getGalleries.

Bug fixes in Photo.getGalleries (flickr.galleries.getListForPhoto):
- Fix loop iterating over empty list (galleries_ -> galleries)
- Fix Person constructor call (add id= keyword argument)
- Fix primary_photo type (Gallery -> Photo)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests added for Batch 6 (10 methods):
- flickr.groups.getInfo
- flickr.groups.search
- flickr.groups.discuss.topics.add
- flickr.groups.discuss.topics.getInfo
- flickr.groups.discuss.topics.getList
- flickr.groups.discuss.replies.add
- flickr.groups.discuss.replies.delete
- flickr.groups.discuss.replies.edit
- flickr.groups.discuss.replies.getInfo
- flickr.groups.discuss.replies.getList

Library bugs fixed:
- Reply.delete was missing return statement
- Reply class was missing edit method
- _format_reply and _format_topic failed when is_pro missing from response
- addDiscussTopic was missing self parameter
- Topic.delete and Topic.edit incorrectly referenced self.topic.id;
  renamed to Topic.deleteReply and Topic.editReply with proper implementation
- getReplies pagination was incorrect (pagination info is in topic element)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tests for flickr.groups.join, joinRequest, leave
- Add tests for flickr.groups.members.getList
- Add tests for flickr.groups.pools.add, getContext, getGroups, getPhotos, remove
- Add tests for flickr.interestingness.getList
- Fix bug in Group.getPoolContext: missing ** for nextphoto kwargs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests for 10 methods:
- Person.findByEmail (flickr.people.findByEmail)
- Person.findByUserName (flickr.people.findByUsername)
- Group.getMemberGroups (flickr.people.getGroups)
- Person.getInfo (flickr.people.getInfo)
- Person.getLimits (flickr.people.getLimits)
- Person.getPhotos (flickr.people.getPhotos)
- Person.getPhotosOf (flickr.people.getPhotosOf)
- Person.getPublicGroups (flickr.people.getPublicGroups)
- Person.getPublicPhotos (flickr.people.getPublicPhotos)
- Person.getUploadStatus (flickr.people.getUploadStatus)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests for flickr.photos.addTags, flickr.photos.delete,
flickr.photos.getAllContexts, flickr.photos.getContactsPhotos,
flickr.photos.getContactsPublicPhotos, flickr.photos.getContext,
flickr.photos.getCounts, flickr.photos.getExif,
flickr.photos.getFavorites, and flickr.photos.getInfo.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Batch 11: Tests for 9 Photo API methods:
- flickr.photos.getNotInSet (Person.getNotInSetPhotos)
- flickr.photos.getPerms (Photo.getPerms)
- flickr.photos.getRecent (Photo.getRecent)
- flickr.photos.getSizes (Photo.getSizes)
- flickr.photos.getUntagged (Photo.getUntagged)
- flickr.photos.getWithGeoData (Photo.getWithGeoData)
- flickr.photos.getWithoutGeoData (Photo.getWithoutGeoData)
- flickr.photos.recentlyUpdated (Photo.recentlyUpdated)
- flickr.photos.search (Photo.search)

Note: flickr.photos.getPopular is NOT implemented in the library
(only Stats.getPopularPhotos exists for flickr.stats.getPopularPhotos).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fixed flickr.photos.recentlyUpdated.json: escaped nested quotes in
  XML attribute value (""text"" -> &quot;text&quot;)
- Added sanitize_xml_response() to scraper to automatically fix
  malformed XML with unescaped quotes in attribute values
- Updated test to use the api-docs file instead of manual JSON

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Batch 12 tests covering:
- flickr.photos.comments.addComment (Photo.addComment)
- flickr.photos.comments.deleteComment (Photo.Comment.delete)
- flickr.photos.comments.editComment (Photo.Comment.edit)
- flickr.photos.comments.getList (Photo.getComments)
- flickr.photos.comments.getRecentForContacts (Photo.Comment.getRecentForContacts)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Batch 13: Tests for flickr.photos.geo.* and flickr.photos.licenses.getInfo

Geo methods tested:
- Person.batchCorrectLocation
- Photo.correctLocation
- Photo.getLocation
- Photo.getGeoPerms
- Photo.photosForLocation
- Photo.removeLocation
- Photo.setContext
- Photo.setLocation
- Photo.setGeoPerms

License methods tested:
- License.getList

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tests for flickr.photos.notes.add, delete, edit
- Add tests for flickr.photos.people.add, delete, deleteCoords, editCoords, getList
- Add tests for flickr.photos.suggestions.approveSuggestion
- Add tests for flickr.photos.licenses.setLicense

Fixes:
- Photo.setLicence: fix unused parameter bug where license was passed but
  never added to args; also handle both License objects and raw IDs
- Photo.getPeople: fix TypeError when single person returned by using
  _check_list() helper; add default token=None parameter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Batch 15:
- flickr.photos.suggestions.getList (Photo.getSuggestions)
- flickr.photos.suggestions.rejectSuggestion (Photo.Suggestion.reject)
- flickr.photos.suggestions.removeSuggestion (Photo.Suggestion.remove)
- flickr.photos.suggestions.suggestLocation (Photo.suggestLocation)
- flickr.photos.removeTag (Tag.remove)
- flickr.photos.setContentType (Photo.setContentType)
- flickr.photos.setDates (Photo.setDates)
- flickr.photos.setMeta (Photo.setMeta)
- flickr.photos.setPerms (Photo.setPerms)
- flickr.photos.setSafetyLevel (Photo.setSafetyLevel)

Fix bug in Photo.getSuggestions where s.pop(Photo(...)) was incorrectly used

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Batch 16: flickr.photos.setTags, flickr.photos.transform.rotate,
flickr.photos.upload.checkTickets

Fix bug in Photo.rotate where format_result accessed wrong key.
The clean_content function renames "_content" to "text" when
other keys exist in the dict, but format_result was looking for
"_content".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Batch 17 tests:
- flickr.photosets.addPhoto
- flickr.photosets.comments.addComment
- flickr.photosets.comments.deleteComment
- flickr.photosets.comments.editComment
- flickr.photosets.comments.getList
- flickr.photosets.create
- flickr.photosets.delete
- flickr.photosets.editMeta
- flickr.photosets.editPhotos
- flickr.photosets.getContext

Fixes discovered bugs in Photoset:
- addComment: Extract comment from r["comment"] like Photo.addComment does
- getComments: Handle empty comments with .get("comment", [])
- getComments: Use photoset=self instead of photo=self for Comment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests for:
- flickr.photosets.getInfo (Photoset.getInfo)
- flickr.photosets.getList (Person.getPhotosets)
- flickr.photosets.getPhotos (Photoset.getPhotos)
- flickr.photosets.orderSets (Photoset.orderSets)
- flickr.photosets.removePhoto (Photoset.removePhoto)
- flickr.photosets.removePhotos (Photoset.removePhotos)
- flickr.photosets.reorderPhotos (Photoset.reorderPhotos)
- flickr.photosets.setPrimaryPhoto (Photoset.setPrimaryPhoto)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests for 10 Place API methods:
- flickr.places.find
- flickr.places.findByLatLon
- flickr.places.getChildrenWithPhotosPublic
- flickr.places.getInfo
- flickr.places.getInfoByUrl
- flickr.places.getPlaceTypes
- flickr.places.getShapeHistory
- flickr.places.getTopPlacesList
- flickr.places.placesForBoundingBox
- flickr.places.placesForContacts

Bug fixes discovered during testing:
- Fix placesForBoundingBox and placesForContacts: Place(dict) -> Place(**dict)
- Fix parse_shapedata: handle single polyline as string vs list
- Fix _extract_place_list: handle single place result, convert text to name

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Place methods tested:
- flickr.places.placesForTags (Place.placesForTags)
- flickr.places.placesForUser (Place.placesForUser)
- flickr.places.tagsForPlace (Place.tagsForPlace static, Place.getTags instance)

Prefs methods tested:
- flickr.prefs.getContentType (prefs.getContentType)
- flickr.prefs.getGeoPerms (prefs.getGeoPerms)
- flickr.prefs.getHidden (prefs.getHidden)
- flickr.prefs.getPrivacy (prefs.getPrivacy)
- flickr.prefs.getSafetyLevel (prefs.getSafetyLevel)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests for:
- flickr.reflection.getMethodInfo (Reflection.getMethodInfo)
- flickr.reflection.getMethods (Reflection.getMethods)
- flickr.stats.getCSVFiles (stats.getCSVFiles)
- flickr.stats.getCollectionDomains (stats.getCollectionDomains)
- flickr.stats.getCollectionReferrers (stats.getCollectionReferrers)
- flickr.stats.getCollectionStats (Collection.getStats)
- flickr.stats.getPhotoDomains (stats.getPhotoDomains)
- flickr.stats.getPhotoReferrers (stats.getPhotoReferrers)
- flickr.stats.getPhotosetDomains (stats.getPhotosetDomains)
- flickr.stats.getPhotosetReferrers (stats.getPhotosetReferrers)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests for:
- flickr.stats.getPhotosetStats (Photoset.getStats)
- flickr.stats.getPhotostreamDomains (Stats.getPhotostreamDomains)
- flickr.stats.getPhotostreamReferrers (Stats.getPhotostreamReferrers)
- flickr.stats.getPhotostreamStats (Stats.getPhotostreamStats)
- flickr.stats.getPopularPhotos (Stats.getPopularPhotos)
- flickr.stats.getTotalViews (Stats.getTotalViews)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests added for:
- Tag.getClusters (flickr.tags.getClusters)
- Tag.Cluster.getPhotos (flickr.tags.getClusterPhotos)
- Photo.getTags (flickr.tags.getListPhoto)
- Tag.getListUserRaw (flickr.tags.getListUserRaw)
- Tag.getRelated (flickr.tags.getRelated)

Bug fix:
- Tag.getListUserRaw now handles single-tag responses correctly
  (XML-to-JSON conversion returns dict instead of list for single items)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Final batch completes API test coverage:
- flickr.test.echo, flickr.test.login, flickr.test.null
- flickr.urls.getGroup, flickr.urls.getUserPhotos, flickr.urls.getUserProfile
- flickr.urls.lookupGallery, flickr.urls.lookupGroup, flickr.urls.lookupUser

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract common test setup code into FlickrApiTestCase base class in
test/base_test.py. This eliminates ~670 lines of duplicated setUp() and
_mock_response() methods across 29 test files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…hotoStats)

These methods were incorrectly listed as "not supported by library" but are
actually implemented:
- flickr.contacts.getPublicList -> Person.getPublicContacts()
- flickr.stats.getPhotoStats -> Photo.getStats(date)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove 102 unused api-docs files (only 123 are needed by tests)
- Remove api-docs/* ignore pattern and exceptions from .gitignore
- The 4 tracked files deleted were never actually loaded by tests
  (tests construct mock responses directly for empty/simple responses)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@beaufour beaufour merged commit e0c1cc4 into master Jan 24, 2026
@beaufour beaufour deleted the extend-testing branch January 24, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant